chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
# Triggers Scope
These rules apply to trigger definitions under `apps/sim/triggers/**`.
- Research the service webhook model before implementing triggers.
- Keep each service under `triggers/{service}/` with barrel exports and event-specific files.
- Use shared helpers for setup instructions, extra fields, and output definitions when multiple triggers in the same service need them.
- Ensure the primary trigger supports switching between trigger types when the integration pattern requires it.
- Keep trigger outputs explicit and useful for downstream blocks.
- Register triggers in `triggers/registry.ts` and keep IDs aligned with the integration naming scheme.
+1
View File
@@ -0,0 +1 @@
export { airtableWebhookTrigger } from './webhook'
+166
View File
@@ -0,0 +1,166 @@
import { AirtableIcon } from '@/components/icons'
import type { TriggerConfig } from '@/triggers/types'
export const airtableWebhookTrigger: TriggerConfig = {
id: 'airtable_webhook',
name: 'Airtable Webhook',
provider: 'airtable',
description:
'Trigger workflow from Airtable record changes like create, update, and delete events (requires Airtable credentials)',
version: '1.0.0',
icon: AirtableIcon,
subBlocks: [
{
id: 'triggerCredentials',
title: 'Credentials',
type: 'oauth-input',
description: 'This trigger requires airtable credentials to access your account.',
serviceId: 'airtable',
requiredScopes: [],
required: true,
mode: 'trigger',
},
{
id: 'baseId',
title: 'Base ID',
type: 'short-input',
placeholder: 'appXXXXXXXXXXXXXX',
description: 'The ID of the Airtable Base this webhook will monitor.',
required: true,
mode: 'trigger',
},
{
id: 'tableId',
title: 'Table ID',
type: 'short-input',
placeholder: 'tblXXXXXXXXXXXXXX',
description: 'The ID of the table within the Base that the webhook will monitor.',
required: true,
mode: 'trigger',
},
{
id: 'includeCellValues',
title: 'Include Full Record Data',
type: 'switch',
description: 'Enable to receive the complete record data in the payload, not just changes.',
defaultValue: false,
mode: 'trigger',
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: [
'Connect your Airtable account using the "Select Airtable credential" button above.',
'Ensure you have provided the correct Base ID and Table ID above.',
'You can find your Base ID and Table ID in the Airtable URL: <code>https://airtable.com/[baseId]/[tableId]/...</code>. See <a href="https://support.airtable.com/docs/finding-airtable-ids" target="_blank" rel="noopener noreferrer">Finding Airtable IDs</a> for details.',
'The webhook will trigger whenever records are created, updated, or deleted in the specified table.',
'Make sure your Airtable account has appropriate permissions for the specified base.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join(''),
mode: 'trigger',
},
],
outputs: {
payloads: {
type: 'array',
description: 'The payloads of the Airtable changes',
items: {
type: 'object',
properties: {
timestamp: { type: 'string', description: 'Timestamp of the change' },
baseTransactionNumber: { type: 'number', description: 'Transaction number' },
},
},
},
latestPayload: {
type: 'object',
description: 'The most recent payload from Airtable',
properties: {
timestamp: { type: 'string', description: 'ISO 8601 timestamp of the change' },
baseTransactionNumber: { type: 'number', description: 'Transaction number' },
payloadFormat: { type: 'string', description: 'Payload format version (e.g., v0)' },
actionMetadata: {
type: 'object',
description: 'Metadata about who made the change',
properties: {
source: {
type: 'string',
description: 'Source of the change (e.g., client, publicApi)',
},
sourceMetadata: {
type: 'object',
description: 'Source metadata including user info',
properties: {
user: {
type: 'object',
description: 'User who made the change',
properties: {
id: { type: 'string', description: 'User ID' },
email: { type: 'string', description: 'User email' },
name: { type: 'string', description: 'User name' },
permissionLevel: { type: 'string', description: 'User permission level' },
},
},
},
},
},
},
changedTablesById: {
type: 'object',
description: 'Tables that were changed (keyed by table ID)',
properties: {
changedRecordsById: {
type: 'object',
description: 'Changed records keyed by record ID',
properties: {
current: {
type: 'object',
description: 'Current state of the record',
properties: {
cellValuesByFieldId: {
type: 'object',
description: 'Cell values keyed by field ID',
},
},
},
},
},
createdRecordsById: { type: 'object', description: 'Created records by ID' },
destroyedRecordIds: { type: 'array', description: 'Array of destroyed record IDs' },
},
},
},
},
airtableChanges: {
type: 'array',
description: 'Changes made to the Airtable table',
items: {
type: 'object',
properties: {
tableId: { type: 'string', description: 'Table ID' },
recordId: { type: 'string', description: 'Record ID' },
changeType: {
type: 'string',
description: 'Type of change (created, changed, destroyed)',
},
cellValuesByFieldId: { type: 'object', description: 'Cell values by field ID' },
},
},
},
},
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,33 @@
import { AshbyIcon } from '@/components/icons'
import { buildApplicationSubmitOutputs, buildAshbySubBlocks } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Application Submitted Trigger
*
* This is the PRIMARY trigger - it includes the dropdown for selecting trigger type.
* Fires when a candidate submits an application or is manually added.
*/
export const ashbyApplicationSubmitTrigger: TriggerConfig = {
id: 'ashby_application_submit',
name: 'Ashby Application Submitted',
provider: 'ashby',
description: 'Trigger workflow when a new application is submitted',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_application_submit',
eventType: 'Application Submitted',
includeDropdown: true,
}),
outputs: buildApplicationSubmitOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,31 @@
import { AshbyIcon } from '@/components/icons'
import { buildAshbySubBlocks, buildCandidateDeleteOutputs } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Candidate Deleted Trigger
*
* Fires when a candidate record is deleted from Ashby.
*/
export const ashbyCandidateDeleteTrigger: TriggerConfig = {
id: 'ashby_candidate_delete',
name: 'Ashby Candidate Deleted',
provider: 'ashby',
description: 'Trigger workflow when a candidate is deleted',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_candidate_delete',
eventType: 'Candidate Deleted',
}),
outputs: buildCandidateDeleteOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+32
View File
@@ -0,0 +1,32 @@
import { AshbyIcon } from '@/components/icons'
import { buildAshbySubBlocks, buildCandidateHireOutputs } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Candidate Hired Trigger
*
* Fires when a candidate is hired. Also triggers applicationUpdate
* and candidateStageChange webhooks.
*/
export const ashbyCandidateHireTrigger: TriggerConfig = {
id: 'ashby_candidate_hire',
name: 'Ashby Candidate Hired',
provider: 'ashby',
description: 'Trigger workflow when a candidate is hired',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_candidate_hire',
eventType: 'Candidate Hired',
}),
outputs: buildCandidateHireOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,32 @@
import { AshbyIcon } from '@/components/icons'
import { buildAshbySubBlocks, buildCandidateStageChangeOutputs } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Candidate Stage Change Trigger
*
* Fires when a candidate moves to a different interview stage.
* Also triggered by candidateHire events.
*/
export const ashbyCandidateStageChangeTrigger: TriggerConfig = {
id: 'ashby_candidate_stage_change',
name: 'Ashby Candidate Stage Change',
provider: 'ashby',
description: 'Trigger workflow when a candidate changes interview stages',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_candidate_stage_change',
eventType: 'Candidate Stage Change',
}),
outputs: buildCandidateStageChangeOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+6
View File
@@ -0,0 +1,6 @@
export { ashbyApplicationSubmitTrigger } from './application_submit'
export { ashbyCandidateDeleteTrigger } from './candidate_delete'
export { ashbyCandidateHireTrigger } from './candidate_hire'
export { ashbyCandidateStageChangeTrigger } from './candidate_stage_change'
export { ashbyJobCreateTrigger } from './job_create'
export { ashbyOfferCreateTrigger } from './offer_create'
+31
View File
@@ -0,0 +1,31 @@
import { AshbyIcon } from '@/components/icons'
import { buildAshbySubBlocks, buildJobCreateOutputs } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Job Created Trigger
*
* Fires when a new job posting is created in Ashby.
*/
export const ashbyJobCreateTrigger: TriggerConfig = {
id: 'ashby_job_create',
name: 'Ashby Job Created',
provider: 'ashby',
description: 'Trigger workflow when a new job is created',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_job_create',
eventType: 'Job Created',
}),
outputs: buildJobCreateOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+31
View File
@@ -0,0 +1,31 @@
import { AshbyIcon } from '@/components/icons'
import { buildAshbySubBlocks, buildOfferCreateOutputs } from '@/triggers/ashby/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Ashby Offer Created Trigger
*
* Fires when a new offer is created for a candidate.
*/
export const ashbyOfferCreateTrigger: TriggerConfig = {
id: 'ashby_offer_create',
name: 'Ashby Offer Created',
provider: 'ashby',
description: 'Trigger workflow when a new offer is created',
version: '1.0.0',
icon: AshbyIcon,
subBlocks: buildAshbySubBlocks({
triggerId: 'ashby_offer_create',
eventType: 'Offer Created',
}),
outputs: buildOfferCreateOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+313
View File
@@ -0,0 +1,313 @@
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Dropdown options for the Ashby trigger type selector.
*/
export const ashbyTriggerOptions = [
{ label: 'Application Submitted', id: 'ashby_application_submit' },
{ label: 'Candidate Stage Change', id: 'ashby_candidate_stage_change' },
{ label: 'Candidate Hired', id: 'ashby_candidate_hire' },
{ label: 'Candidate Deleted', id: 'ashby_candidate_delete' },
{ label: 'Job Created', id: 'ashby_job_create' },
{ label: 'Offer Created', id: 'ashby_offer_create' },
]
/**
* Maps Sim trigger IDs to Ashby webhookType / event action values.
* Used by webhook.create body and matchEvent filtering.
*/
export const ASHBY_TRIGGER_ACTION_MAP: Record<string, string> = {
ashby_application_submit: 'applicationSubmit',
ashby_candidate_stage_change: 'candidateStageChange',
ashby_candidate_hire: 'candidateHire',
ashby_candidate_delete: 'candidateDelete',
ashby_job_create: 'jobCreate',
ashby_offer_create: 'offerCreate',
}
/**
* Checks if an Ashby webhook event matches the configured trigger.
* Ashby sends a ping event on webhook create/edit; this filter rejects
* any event whose `action` does not equal the expected webhookType.
*/
export function isAshbyEventMatch(triggerId: string, action: string): boolean {
const expected = ASHBY_TRIGGER_ACTION_MAP[triggerId]
if (!expected) return false
return expected === action
}
/**
* Generates setup instructions for Ashby webhooks.
* Webhooks are automatically created/deleted via the Ashby API.
*/
export function ashbySetupInstructions(eventType: string): string {
const instructions = [
'Enter your Ashby API Key above. You can find your API key in Ashby at <strong>Settings &gt; API Keys</strong>.',
`The webhook for <strong>${eventType}</strong> events will be automatically created in Ashby when you save the trigger.`,
'The webhook will be automatically deleted if you remove this trigger.',
]
return instructions
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Builds the complete subBlocks array for an Ashby trigger.
* Ashby webhooks are managed via API, so no webhook URL is displayed.
*
* Structure: [dropdown?] -> apiKey -> instructions
*/
export function buildAshbySubBlocks(options: {
triggerId: string
eventType: string
includeDropdown?: boolean
}): SubBlockConfig[] {
const { triggerId, eventType, includeDropdown = false } = options
const blocks: SubBlockConfig[] = []
if (includeDropdown) {
blocks.push({
id: 'selectedTriggerId',
title: 'Trigger Type',
type: 'dropdown',
mode: 'trigger',
options: ashbyTriggerOptions,
value: () => triggerId,
required: true,
})
}
blocks.push({
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Ashby API key',
password: true,
required: true,
paramVisibility: 'user-only',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
})
blocks.push({
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: ashbySetupInstructions(eventType),
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
})
return blocks
}
/**
* Core fields present in all Ashby webhook payloads.
*/
const coreOutputs = {
action: {
type: 'string',
description: 'The webhook event type (e.g., applicationSubmit, candidateHire)',
},
} as const
/**
* Build outputs for applicationSubmit events.
* Payload: { action, data: { application: { id, createdAt, updatedAt, status,
* candidate: { id, name }, currentInterviewStage: { id, title },
* job: { id, title } } } }
*/
export function buildApplicationSubmitOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
application: {
id: { type: 'string', description: 'Application UUID' },
createdAt: { type: 'string', description: 'Application creation timestamp (ISO 8601)' },
updatedAt: {
type: 'string',
description: 'Application last update timestamp (ISO 8601)',
},
status: {
type: 'string',
description: 'Application status (Active, Hired, Archived, Lead)',
},
candidate: {
id: { type: 'string', description: 'Candidate UUID' },
name: { type: 'string', description: 'Candidate name' },
},
currentInterviewStage: {
id: { type: 'string', description: 'Current interview stage UUID' },
title: { type: 'string', description: 'Current interview stage title' },
stageType: {
type: 'string',
description: 'Current interview stage type (e.g., Lead, Applied, Interview, Offer)',
},
},
job: {
id: { type: 'string', description: 'Job UUID' },
title: { type: 'string', description: 'Job title' },
},
},
} as Record<string, TriggerOutput>
}
/**
* Build outputs for candidateStageChange events.
* Payload matches the application object structure (same as applicationUpdate).
* Payload: { action, data: { application: { id, createdAt, updatedAt, status,
* candidate: { id, name }, currentInterviewStage: { id, title, type },
* job: { id, title } } } }
*/
export function buildCandidateStageChangeOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
application: {
id: { type: 'string', description: 'Application UUID' },
createdAt: { type: 'string', description: 'Application creation timestamp (ISO 8601)' },
updatedAt: {
type: 'string',
description: 'Application last update timestamp (ISO 8601)',
},
status: {
type: 'string',
description: 'Application status (Active, Hired, Archived, Lead)',
},
candidate: {
id: { type: 'string', description: 'Candidate UUID' },
name: { type: 'string', description: 'Candidate name' },
},
currentInterviewStage: {
id: { type: 'string', description: 'Current interview stage UUID' },
title: { type: 'string', description: 'Current interview stage title' },
stageType: {
type: 'string',
description: 'Current interview stage type (e.g., Lead, Applied, Interview, Offer)',
},
},
job: {
id: { type: 'string', description: 'Job UUID' },
title: { type: 'string', description: 'Job title' },
},
},
} as Record<string, TriggerOutput>
}
/**
* Build outputs for candidateHire events.
* Per Ashby docs, candidateHire payloads include application details and most
* recent accepted offer information.
*/
export function buildCandidateHireOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
application: {
id: { type: 'string', description: 'Application UUID' },
createdAt: { type: 'string', description: 'Application creation timestamp (ISO 8601)' },
updatedAt: {
type: 'string',
description: 'Application last update timestamp (ISO 8601)',
},
status: { type: 'string', description: 'Application status (Hired)' },
candidate: {
id: { type: 'string', description: 'Candidate UUID' },
name: { type: 'string', description: 'Candidate name' },
},
currentInterviewStage: {
id: { type: 'string', description: 'Current interview stage UUID' },
title: { type: 'string', description: 'Current interview stage title' },
stageType: {
type: 'string',
description: 'Current interview stage type (e.g., Lead, Applied, Interview, Offer)',
},
},
job: {
id: { type: 'string', description: 'Job UUID' },
title: { type: 'string', description: 'Job title' },
},
},
offer: {
id: { type: 'string', description: 'Accepted offer UUID' },
applicationId: { type: 'string', description: 'Associated application UUID' },
acceptanceStatus: { type: 'string', description: 'Offer acceptance status' },
offerStatus: { type: 'string', description: 'Offer process status' },
decidedAt: {
type: 'string',
description: 'Offer decision timestamp (ISO 8601)',
},
latestVersion: {
id: { type: 'string', description: 'Latest offer version UUID' },
},
},
} as Record<string, TriggerOutput>
}
/**
* Build outputs for candidateDelete events.
* Payload: { action, data: { candidate: { id } } }
*/
export function buildCandidateDeleteOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
candidate: {
id: { type: 'string', description: 'Deleted candidate UUID' },
},
} as Record<string, TriggerOutput>
}
/**
* Build outputs for jobCreate events.
* Payload: { action, data: { job: { id, title, confidential, status, employmentType } } }
*/
export function buildJobCreateOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
job: {
id: { type: 'string', description: 'Job UUID' },
title: { type: 'string', description: 'Job title' },
confidential: { type: 'boolean', description: 'Whether the job is confidential' },
status: { type: 'string', description: 'Job status (Open, Closed, Draft, Archived)' },
employmentType: {
type: 'string',
description: 'Employment type (FullTime, PartTime, Intern, Contract)',
},
},
} as Record<string, TriggerOutput>
}
/**
* Build outputs for offerCreate events.
* Payload: { action, data: { offer: { id, decidedAt, applicationId, acceptanceStatus,
* offerStatus, latestVersion: { id } } } }
*/
export function buildOfferCreateOutputs(): Record<string, TriggerOutput> {
return {
...coreOutputs,
offer: {
id: { type: 'string', description: 'Offer UUID' },
applicationId: { type: 'string', description: 'Associated application UUID' },
acceptanceStatus: {
type: 'string',
description: 'Offer acceptance status (Accepted, Declined, Pending, Created, Cancelled)',
},
offerStatus: {
type: 'string',
description:
'Offer process status (WaitingOnApprovalStart, WaitingOnOfferApproval, WaitingOnApprovalDefinition, WaitingOnCandidateResponse, CandidateRejected, CandidateAccepted, OfferCancelled)',
},
decidedAt: {
type: 'string',
description:
'Offer decision timestamp (ISO 8601). Typically null at creation; populated after candidate responds.',
},
latestVersion: {
id: { type: 'string', description: 'Latest offer version UUID' },
},
},
} as Record<string, TriggerOutput>
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildCommentOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Comment Created Trigger
*
* Triggers when a comment is created in Attio.
*/
export const attioCommentCreatedTrigger: TriggerConfig = {
id: 'attio_comment_created',
name: 'Attio Comment Created',
provider: 'attio',
description: 'Trigger workflow when a new comment is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_comment_created'),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildCommentOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Comment Deleted Trigger
*
* Triggers when a comment is deleted in Attio.
*/
export const attioCommentDeletedTrigger: TriggerConfig = {
id: 'attio_comment_deleted',
name: 'Attio Comment Deleted',
provider: 'attio',
description: 'Trigger workflow when a comment is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_comment_deleted'),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildCommentOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Comment Resolved Trigger
*
* Triggers when a comment thread is resolved in Attio.
*/
export const attioCommentResolvedTrigger: TriggerConfig = {
id: 'attio_comment_resolved',
name: 'Attio Comment Resolved',
provider: 'attio',
description: 'Trigger workflow when a comment thread is resolved in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_comment_resolved'),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildCommentOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Comment Unresolved Trigger
*
* Triggers when a comment thread is unresolved in Attio.
*/
export const attioCommentUnresolvedTrigger: TriggerConfig = {
id: 'attio_comment_unresolved',
name: 'Attio Comment Unresolved',
provider: 'attio',
description: 'Trigger workflow when a comment thread is unresolved in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_comment_unresolved'),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+22
View File
@@ -0,0 +1,22 @@
export { attioCommentCreatedTrigger } from './comment_created'
export { attioCommentDeletedTrigger } from './comment_deleted'
export { attioCommentResolvedTrigger } from './comment_resolved'
export { attioCommentUnresolvedTrigger } from './comment_unresolved'
export { attioListCreatedTrigger } from './list_created'
export { attioListDeletedTrigger } from './list_deleted'
export { attioListEntryCreatedTrigger } from './list_entry_created'
export { attioListEntryDeletedTrigger } from './list_entry_deleted'
export { attioListEntryUpdatedTrigger } from './list_entry_updated'
export { attioListUpdatedTrigger } from './list_updated'
export { attioNoteCreatedTrigger } from './note_created'
export { attioNoteDeletedTrigger } from './note_deleted'
export { attioNoteUpdatedTrigger } from './note_updated'
export { attioRecordCreatedTrigger } from './record_created'
export { attioRecordDeletedTrigger } from './record_deleted'
export { attioRecordMergedTrigger } from './record_merged'
export { attioRecordUpdatedTrigger } from './record_updated'
export { attioTaskCreatedTrigger } from './task_created'
export { attioTaskDeletedTrigger } from './task_deleted'
export { attioTaskUpdatedTrigger } from './task_updated'
export { attioWebhookTrigger } from './webhook'
export { attioWorkspaceMemberCreatedTrigger } from './workspace_member_created'
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Created Trigger
*
* Triggers when a list is created in Attio.
*/
export const attioListCreatedTrigger: TriggerConfig = {
id: 'attio_list_created',
name: 'Attio List Created',
provider: 'attio',
description: 'Trigger workflow when a list is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_created'),
outputs: buildListOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Deleted Trigger
*
* Triggers when a list is deleted in Attio.
*/
export const attioListDeletedTrigger: TriggerConfig = {
id: 'attio_list_deleted',
name: 'Attio List Deleted',
provider: 'attio',
description: 'Trigger workflow when a list is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_deleted'),
outputs: buildListOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListEntryOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Entry Created Trigger
*
* Triggers when a list entry is created in Attio.
*/
export const attioListEntryCreatedTrigger: TriggerConfig = {
id: 'attio_list_entry_created',
name: 'Attio List Entry Created',
provider: 'attio',
description: 'Trigger workflow when a new list entry is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_entry_created'),
outputs: buildListEntryOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListEntryOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Entry Deleted Trigger
*
* Triggers when a list entry is deleted in Attio.
*/
export const attioListEntryDeletedTrigger: TriggerConfig = {
id: 'attio_list_entry_deleted',
name: 'Attio List Entry Deleted',
provider: 'attio',
description: 'Trigger workflow when a list entry is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_entry_deleted'),
outputs: buildListEntryOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListEntryUpdatedOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Entry Updated Trigger
*
* Triggers when a list entry is updated in Attio.
*/
export const attioListEntryUpdatedTrigger: TriggerConfig = {
id: 'attio_list_entry_updated',
name: 'Attio List Entry Updated',
provider: 'attio',
description: 'Trigger workflow when a list entry is updated in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_entry_updated'),
outputs: buildListEntryUpdatedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio List Updated Trigger
*
* Triggers when a list is updated in Attio.
*/
export const attioListUpdatedTrigger: TriggerConfig = {
id: 'attio_list_updated',
name: 'Attio List Updated',
provider: 'attio',
description: 'Trigger workflow when a list is updated in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_list_updated'),
outputs: buildListOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildNoteOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Note Created Trigger
*
* Triggers when a note is created in Attio.
*/
export const attioNoteCreatedTrigger: TriggerConfig = {
id: 'attio_note_created',
name: 'Attio Note Created',
provider: 'attio',
description: 'Trigger workflow when a new note is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_note_created'),
outputs: buildNoteOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildNoteOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Note Deleted Trigger
*
* Triggers when a note is deleted in Attio.
*/
export const attioNoteDeletedTrigger: TriggerConfig = {
id: 'attio_note_deleted',
name: 'Attio Note Deleted',
provider: 'attio',
description: 'Trigger workflow when a note is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_note_deleted'),
outputs: buildNoteOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildNoteOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Note Updated Trigger
*
* Triggers when a note is updated in Attio.
*/
export const attioNoteUpdatedTrigger: TriggerConfig = {
id: 'attio_note_updated',
name: 'Attio Note Updated',
provider: 'attio',
description: 'Trigger workflow when a note is updated in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_note_updated'),
outputs: buildNoteOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+45
View File
@@ -0,0 +1,45 @@
import { AttioIcon } from '@/components/icons'
import {
attioTriggerOptions,
buildAttioTriggerSubBlocks,
buildRecordOutputs,
} from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Record Created Trigger
*
* This is the PRIMARY trigger - it includes the dropdown for selecting trigger type.
* Triggers when a new record is created in Attio.
*/
export const attioRecordCreatedTrigger: TriggerConfig = {
id: 'attio_record_created',
name: 'Attio Record Created',
provider: 'attio',
description: 'Trigger workflow when a new record is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: [
{
id: 'selectedTriggerId',
title: 'Trigger Type',
type: 'dropdown',
mode: 'trigger',
options: attioTriggerOptions,
value: () => 'attio_record_created',
required: true,
},
...buildAttioTriggerSubBlocks('attio_record_created'),
],
outputs: buildRecordOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildRecordOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Record Deleted Trigger
*
* Triggers when a record is deleted in Attio.
*/
export const attioRecordDeletedTrigger: TriggerConfig = {
id: 'attio_record_deleted',
name: 'Attio Record Deleted',
provider: 'attio',
description: 'Trigger workflow when a record is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_record_deleted'),
outputs: buildRecordOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildRecordMergedEventOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Record Merged Trigger
*
* Triggers when two records are merged in Attio.
*/
export const attioRecordMergedTrigger: TriggerConfig = {
id: 'attio_record_merged',
name: 'Attio Record Merged',
provider: 'attio',
description: 'Trigger workflow when two records are merged in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_record_merged'),
outputs: buildRecordMergedEventOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildRecordUpdatedOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Record Updated Trigger
*
* Triggers when a record is updated in Attio.
*/
export const attioRecordUpdatedTrigger: TriggerConfig = {
id: 'attio_record_updated',
name: 'Attio Record Updated',
provider: 'attio',
description: 'Trigger workflow when a record is updated in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_record_updated'),
outputs: buildRecordUpdatedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildTaskOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Task Created Trigger
*
* Triggers when a task is created in Attio.
*/
export const attioTaskCreatedTrigger: TriggerConfig = {
id: 'attio_task_created',
name: 'Attio Task Created',
provider: 'attio',
description: 'Trigger workflow when a new task is created in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_task_created'),
outputs: buildTaskOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildTaskOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Task Deleted Trigger
*
* Triggers when a task is deleted in Attio.
*/
export const attioTaskDeletedTrigger: TriggerConfig = {
id: 'attio_task_deleted',
name: 'Attio Task Deleted',
provider: 'attio',
description: 'Trigger workflow when a task is deleted in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_task_deleted'),
outputs: buildTaskOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildTaskOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Task Updated Trigger
*
* Triggers when a task is updated in Attio.
*/
export const attioTaskUpdatedTrigger: TriggerConfig = {
id: 'attio_task_updated',
name: 'Attio Task Updated',
provider: 'attio',
description: 'Trigger workflow when a task is updated in Attio',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_task_updated'),
outputs: buildTaskOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
+527
View File
@@ -0,0 +1,527 @@
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
export const attioTriggerOptions = [
{ label: 'Record Created', id: 'attio_record_created' },
{ label: 'Record Updated', id: 'attio_record_updated' },
{ label: 'Record Deleted', id: 'attio_record_deleted' },
{ label: 'Record Merged', id: 'attio_record_merged' },
{ label: 'Note Created', id: 'attio_note_created' },
{ label: 'Note Updated', id: 'attio_note_updated' },
{ label: 'Note Deleted', id: 'attio_note_deleted' },
{ label: 'Task Created', id: 'attio_task_created' },
{ label: 'Task Updated', id: 'attio_task_updated' },
{ label: 'Task Deleted', id: 'attio_task_deleted' },
{ label: 'Comment Created', id: 'attio_comment_created' },
{ label: 'Comment Resolved', id: 'attio_comment_resolved' },
{ label: 'Comment Unresolved', id: 'attio_comment_unresolved' },
{ label: 'Comment Deleted', id: 'attio_comment_deleted' },
{ label: 'List Entry Created', id: 'attio_list_entry_created' },
{ label: 'List Entry Updated', id: 'attio_list_entry_updated' },
{ label: 'List Entry Deleted', id: 'attio_list_entry_deleted' },
{ label: 'List Created', id: 'attio_list_created' },
{ label: 'List Updated', id: 'attio_list_updated' },
{ label: 'List Deleted', id: 'attio_list_deleted' },
{ label: 'Workspace Member Created', id: 'attio_workspace_member_created' },
{ label: 'Generic Webhook (All Events)', id: 'attio_webhook' },
]
export function attioSetupInstructions(): string {
const instructions = [
'<strong>Note:</strong> Webhooks are automatically created in Attio when you deploy this workflow, and deleted when you undeploy. See the <a href="https://docs.attio.com/rest-api/guides/webhooks" target="_blank" rel="noopener noreferrer">Attio webhook documentation</a> for details.',
'Connect your <strong>Attio account</strong> using the credential selector above.',
'<strong>Deploy</strong> the workflow — a webhook will be created automatically in your Attio workspace.',
]
return instructions
.map(
(instruction, index) =>
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
)
.join('')
}
/**
* Builds subBlocks for an Attio trigger with OAuth credentials and automatic webhook lifecycle.
* Used by both the primary trigger (with dropdown) and secondary triggers.
*/
export function buildAttioTriggerSubBlocks(triggerId: string): SubBlockConfig[] {
return [
{
id: 'triggerCredentials',
title: 'Attio Account',
type: 'oauth-input',
serviceId: 'attio',
mode: 'trigger',
required: true,
condition: { field: 'selectedTriggerId', value: triggerId },
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: attioSetupInstructions(),
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
/**
* Base webhook outputs common to all Attio triggers.
*/
function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
return {
eventType: {
type: 'string',
description: 'The type of event (e.g. record.created, note.created)',
},
}
}
/**
* Record event outputs for record triggers.
*/
function buildRecordIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
objectId: { type: 'string', description: 'The object type ID (e.g. people, companies)' },
recordId: { type: 'string', description: 'The record ID' },
}
}
/**
* Record updated event outputs (includes attributeId).
*/
function buildRecordUpdatedIdOutputs(): Record<string, TriggerOutput> {
return {
...buildRecordIdOutputs(),
attributeId: {
type: 'string',
description: 'The ID of the attribute that was updated on the record',
},
}
}
/**
* Record merged event outputs.
* Attio payload: id.record_id (winner), duplicate_object_id, duplicate_record_id (loser).
*/
function buildRecordMergedOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
objectId: { type: 'string', description: 'The object type ID of the surviving record' },
recordId: { type: 'string', description: 'The surviving record ID after merge' },
duplicateObjectId: {
type: 'string',
description: 'The object type ID of the merged-away record',
},
duplicateRecordId: { type: 'string', description: 'The record ID that was merged away' },
}
}
/**
* Note event outputs.
*/
function buildNoteIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
noteId: { type: 'string', description: 'The note ID' },
parentObjectId: { type: 'string', description: 'The parent object type ID' },
parentRecordId: { type: 'string', description: 'The parent record ID' },
}
}
/**
* Task event outputs.
* Attio task webhook payloads only contain workspace_id and task_id.
*/
function buildTaskIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
taskId: { type: 'string', description: 'The task ID' },
}
}
/**
* Comment event outputs.
* Attio payload uses object_id/record_id (not parent_*), plus list_id/entry_id.
*/
function buildCommentIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
threadId: { type: 'string', description: 'The thread ID' },
commentId: { type: 'string', description: 'The comment ID' },
objectId: { type: 'string', description: 'The object type ID' },
recordId: { type: 'string', description: 'The record ID' },
listId: { type: 'string', description: 'The list ID (if comment is on a list entry)' },
entryId: { type: 'string', description: 'The list entry ID (if comment is on a list entry)' },
}
}
/**
* List entry event outputs.
*/
function buildListEntryIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
listId: { type: 'string', description: 'The list ID' },
entryId: { type: 'string', description: 'The list entry ID' },
}
}
/**
* List entry updated event outputs (includes attributeId).
*/
function buildListEntryUpdatedIdOutputs(): Record<string, TriggerOutput> {
return {
...buildListEntryIdOutputs(),
attributeId: {
type: 'string',
description: 'The ID of the attribute that was updated on the list entry',
},
}
}
/** Record created/deleted outputs. */
export function buildRecordOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildRecordIdOutputs(),
}
}
/** Record updated outputs (includes attributeId). */
export function buildRecordUpdatedOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildRecordUpdatedIdOutputs(),
}
}
/** Record merged outputs. */
export function buildRecordMergedEventOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildRecordMergedOutputs(),
}
}
/** Note event outputs. */
export function buildNoteOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildNoteIdOutputs(),
}
}
/** Task event outputs. */
export function buildTaskOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildTaskIdOutputs(),
}
}
/** Comment event outputs. */
export function buildCommentOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildCommentIdOutputs(),
}
}
/**
* List event outputs.
*/
function buildListIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
listId: { type: 'string', description: 'The list ID' },
}
}
/** List created/updated/deleted outputs. */
export function buildListOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildListIdOutputs(),
}
}
/**
* Workspace member event outputs.
*/
function buildWorkspaceMemberIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
workspaceMemberId: { type: 'string', description: 'The workspace member ID' },
}
}
/** Workspace member outputs. */
export function buildWorkspaceMemberOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildWorkspaceMemberIdOutputs(),
}
}
/** List entry created/deleted outputs. */
export function buildListEntryOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildListEntryIdOutputs(),
}
}
/** List entry updated outputs (includes attributeId). */
export function buildListEntryUpdatedOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildListEntryUpdatedIdOutputs(),
}
}
/** Generic webhook outputs covering all event types. */
export function buildGenericWebhookOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
id: { type: 'json', description: 'The event ID object containing resource identifiers' },
parentObjectId: {
type: 'string',
description: 'The parent object type ID (if applicable)',
},
parentRecordId: {
type: 'string',
description: 'The parent record ID (if applicable)',
},
}
}
/**
* Maps trigger IDs to the exact Attio event type strings.
*/
export const TRIGGER_EVENT_MAP: Record<string, string[]> = {
attio_record_created: ['record.created'],
attio_record_updated: ['record.updated'],
attio_record_deleted: ['record.deleted'],
attio_record_merged: ['record.merged'],
attio_note_created: ['note.created'],
attio_note_updated: ['note.updated', 'note-content.updated'],
attio_note_deleted: ['note.deleted'],
attio_task_created: ['task.created'],
attio_task_updated: ['task.updated'],
attio_task_deleted: ['task.deleted'],
attio_comment_created: ['comment.created'],
attio_comment_resolved: ['comment.resolved'],
attio_comment_unresolved: ['comment.unresolved'],
attio_comment_deleted: ['comment.deleted'],
attio_list_entry_created: ['list-entry.created'],
attio_list_entry_updated: ['list-entry.updated'],
attio_list_entry_deleted: ['list-entry.deleted'],
attio_list_created: ['list.created'],
attio_list_updated: ['list.updated'],
attio_list_deleted: ['list.deleted'],
attio_workspace_member_created: ['workspace-member.created'],
}
/**
* Extracts the first event from an Attio webhook payload.
* Attio wraps events in an `events` array: `{ webhook_id, events: [{ event_type, id, ... }] }`.
*/
export function getAttioEvent(body: Record<string, unknown>): Record<string, unknown> | undefined {
const events = body.events as Array<Record<string, unknown>> | undefined
return events?.[0]
}
/**
* Checks if an Attio webhook payload matches a trigger.
*/
export function isAttioPayloadMatch(triggerId: string, body: Record<string, unknown>): boolean {
if (triggerId === 'attio_webhook') {
return true
}
const event = getAttioEvent(body)
const eventType = event?.event_type as string | undefined
if (!eventType) {
return false
}
const acceptedEvents = TRIGGER_EVENT_MAP[triggerId]
return acceptedEvents ? acceptedEvents.includes(eventType) : false
}
/**
* Extracts formatted data from an Attio record event payload.
* Used for record.created, record.deleted triggers.
*/
export function extractAttioRecordData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
objectId: id.object_id ?? null,
recordId: id.record_id ?? null,
}
}
/**
* Extracts formatted data from an Attio record.updated event payload.
*/
export function extractAttioRecordUpdatedData(
body: Record<string, unknown>
): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
objectId: id.object_id ?? null,
recordId: id.record_id ?? null,
attributeId: id.attribute_id ?? null,
}
}
/**
* Extracts formatted data from an Attio record.merged event payload.
*/
export function extractAttioRecordMergedData(
body: Record<string, unknown>
): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
objectId: id.object_id ?? null,
recordId: id.record_id ?? null,
duplicateObjectId: (event.duplicate_object_id as string) ?? null,
duplicateRecordId: (event.duplicate_record_id as string) ?? null,
}
}
/**
* Extracts formatted data from an Attio note event payload.
*/
export function extractAttioNoteData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
noteId: id.note_id ?? null,
parentObjectId: (event.parent_object_id as string) ?? null,
parentRecordId: (event.parent_record_id as string) ?? null,
}
}
/**
* Extracts formatted data from an Attio task event payload.
*/
export function extractAttioTaskData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
taskId: id.task_id ?? null,
}
}
/**
* Extracts formatted data from an Attio comment event payload.
*/
export function extractAttioCommentData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
threadId: (event.thread_id as string) ?? null,
commentId: id.comment_id ?? null,
objectId: (event.object_id as string) ?? null,
recordId: (event.record_id as string) ?? null,
listId: (event.list_id as string) ?? null,
entryId: (event.entry_id as string) ?? null,
}
}
/**
* Extracts formatted data from an Attio list-entry event payload.
* Used for list-entry.created, list-entry.deleted triggers.
*/
export function extractAttioListEntryData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
listId: id.list_id ?? null,
entryId: id.entry_id ?? null,
}
}
/**
* Extracts formatted data from an Attio list-entry.updated event payload.
*/
export function extractAttioListEntryUpdatedData(
body: Record<string, unknown>
): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
listId: id.list_id ?? null,
entryId: id.entry_id ?? null,
attributeId: id.attribute_id ?? null,
}
}
/**
* Extracts formatted data from an Attio list event payload.
* Used for list.created, list.updated, list.deleted triggers.
*/
export function extractAttioListData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
listId: id.list_id ?? null,
}
}
/**
* Extracts formatted data from an Attio workspace-member.created event payload.
*/
export function extractAttioWorkspaceMemberData(
body: Record<string, unknown>
): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
workspaceId: id.workspace_id ?? null,
workspaceMemberId: id.workspace_member_id ?? null,
}
}
/**
* Extracts formatted data from a generic Attio webhook payload.
* Passes through the first event with camelCase field mapping.
*/
export function extractAttioGenericData(body: Record<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
return {
eventType: event.event_type ?? null,
id,
parentObjectId: (event.parent_object_id as string) ?? null,
parentRecordId: (event.parent_record_id as string) ?? null,
}
}
+29
View File
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildGenericWebhookOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Generic Attio Webhook Trigger
*
* Captures all Attio webhook events without filtering.
*/
export const attioWebhookTrigger: TriggerConfig = {
id: 'attio_webhook',
name: 'Attio Webhook (All Events)',
provider: 'attio',
description: 'Trigger workflow on any Attio webhook event',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_webhook'),
outputs: buildGenericWebhookOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,29 @@
import { AttioIcon } from '@/components/icons'
import { buildAttioTriggerSubBlocks, buildWorkspaceMemberOutputs } from '@/triggers/attio/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Attio Workspace Member Created Trigger
*
* Triggers when a new member is added to the Attio workspace.
*/
export const attioWorkspaceMemberCreatedTrigger: TriggerConfig = {
id: 'attio_workspace_member_created',
name: 'Attio Workspace Member Created',
provider: 'attio',
description: 'Trigger workflow when a new member is added to the Attio workspace',
version: '1.0.0',
icon: AttioIcon,
subBlocks: buildAttioTriggerSubBlocks('attio_workspace_member_created'),
outputs: buildWorkspaceMemberOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Attio-Signature': 'hmac-sha256-signature',
},
},
}
@@ -0,0 +1,34 @@
import { AzureIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
azureDevOpsTriggerOptions,
buildBuildFailedOutputs,
buildFailedSetupInstructions,
} from '@/triggers/azure_devops/utils'
import type { TriggerConfig } from '@/triggers/types'
export const azureDevOpsBuildFailedTrigger: TriggerConfig = {
id: 'azure_devops_build_failed',
name: 'Azure DevOps Build Failed',
provider: 'azure_devops',
description:
'Trigger workflow when an Azure DevOps build fails, is canceled, or partially succeeds',
version: '1.0.0',
icon: AzureIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'azure_devops_build_failed',
triggerOptions: azureDevOpsTriggerOptions,
includeDropdown: true,
setupInstructions: buildFailedSetupInstructions,
}),
outputs: buildBuildFailedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+3
View File
@@ -0,0 +1,3 @@
export { azureDevOpsBuildFailedTrigger } from './build_failed'
export { azureDevOpsWebhookTrigger } from './webhook'
export { azureDevOpsWorkItemCreatedTrigger } from './work_item_created'
+283
View File
@@ -0,0 +1,283 @@
import type { TriggerOutput } from '@/triggers/types'
export const azureDevOpsTriggerOptions = [
{ label: 'Build Failed', id: 'azure_devops_build_failed' },
{ label: 'Work Item Created', id: 'azure_devops_work_item_created' },
{ label: 'All Service Hook Events', id: 'azure_devops_webhook' },
]
export const AZURE_DEVOPS_BUILD_FAILED_EVENT = 'build.complete'
export const AZURE_DEVOPS_WORK_ITEM_CREATED_EVENT = 'workitem.created'
function instructions(steps: string[]): string {
return steps.map((s, i) => `<div class="mb-3"><strong>${i + 1}.</strong> ${s}</div>`).join('')
}
export const buildFailedSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'For <strong>Trigger on this type of event</strong>, select <strong>Build completed</strong>.',
'Under <strong>Filters</strong>, set <strong>Build result</strong> to <strong>Failed</strong> (optionally add Canceled / Partially succeeded).',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
])
export const workItemCreatedSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'For <strong>Trigger on this type of event</strong>, select <strong>Work item created</strong>.',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
])
export const webhookSetupInstructions = instructions([
'Open your Azure DevOps project and go to <strong>Project settings → Service hooks</strong>.',
'Click <strong>+ Create subscription</strong>, choose <strong>Web Hooks</strong>, then <strong>Next</strong>.',
'Select whichever <strong>event types</strong> you want this URL to receive (build, work item, release, etc.).',
'Click <strong>Next</strong>, paste the <strong>Webhook URL</strong> above into the <strong>URL</strong> field.',
'Leave other fields as defaults. Click <strong>Test</strong> to verify, then <strong>Finish</strong>.',
'Sim does not filter deliveries for this trigger — configure event types in Azure DevOps.',
])
/**
* Returns whether an Azure DevOps service hook payload matches the configured trigger.
*/
export function isAzureDevOpsEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
if (triggerId === 'azure_devops_webhook') {
return true
}
const eventType = body.eventType as string | undefined
if (triggerId === 'azure_devops_build_failed') {
if (eventType !== AZURE_DEVOPS_BUILD_FAILED_EVENT) {
return false
}
const resource = body.resource as Record<string, unknown> | undefined
const result = (resource?.result as string | undefined)?.toLowerCase()
return (
result === 'failed' ||
result === 'canceled' ||
result === 'cancelled' ||
result === 'stopped' ||
result === 'partiallysucceeded'
)
}
if (triggerId === 'azure_devops_work_item_created') {
return eventType === AZURE_DEVOPS_WORK_ITEM_CREATED_EVENT
}
return false
}
export function buildBuildFailedOutputs(): Record<string, TriggerOutput> {
return {
buildId: {
type: 'number',
description: 'Build ID',
},
buildNumber: {
type: 'string',
description: 'Build number string (e.g. 20240101.1)',
},
result: {
type: 'string',
description: 'Build result: failed | canceled | partiallySucceeded',
},
pipelineId: {
type: 'number',
description: 'Pipeline definition ID',
},
pipelineName: {
type: 'string',
description: 'Pipeline definition name',
},
projectName: {
type: 'string',
description: 'Azure DevOps project name',
},
branch: {
type: 'string',
description: 'Source branch name (refs/heads/ prefix stripped)',
},
commitSha: {
type: 'string',
description: 'Source commit SHA',
},
triggeredBy: {
type: 'string',
description: 'Display name of the person who triggered the build',
},
triggeredByEmail: {
type: 'string',
description: 'Email/unique name of the person who triggered the build, or null if not set',
},
startTime: {
type: 'string',
description: 'Build start time (ISO 8601)',
},
finishTime: {
type: 'string',
description: 'Build finish time (ISO 8601)',
},
buildUrl: {
type: 'string',
description: 'API URL for the build resource',
},
}
}
export function buildWorkItemCreatedOutputs(): Record<string, TriggerOutput> {
return {
workItemId: {
type: 'number',
description: 'Work item ID',
},
workItemType: {
type: 'string',
description: 'Work item type for Basic process (e.g. Issue, Task, Epic)',
},
title: {
type: 'string',
description: 'Work item title',
},
state: {
type: 'string',
description: 'Work item state for Basic process (e.g. To Do, Doing, Done)',
},
createdBy: {
type: 'string',
description: 'Display name of the creator, or null if not set',
},
assignedTo: {
type: 'string',
description: 'Assignee display name, or null if unassigned',
},
priority: {
type: 'number',
description: 'Priority (14), or 0 if not set',
},
areaPath: {
type: 'string',
description: 'Area path',
},
iterationPath: {
type: 'string',
description: 'Iteration path',
},
description: {
type: 'string',
description: 'Work item description (HTML), or null if not set',
},
projectName: {
type: 'string',
description: 'Azure DevOps project name',
},
workItemUrl: {
type: 'string',
description: 'API URL for the work item resource',
},
}
}
export function buildWebhookOutputs(): Record<string, TriggerOutput> {
return {
eventType: {
type: 'string',
description: 'Service hook event type (e.g. build.complete, workitem.created)',
},
notificationId: {
type: 'number',
description: 'Notification ID',
},
subscriptionId: {
type: 'string',
description: 'Service hook subscription ID',
},
publisherId: {
type: 'string',
description: 'Publisher ID (e.g. tfs)',
},
createdDate: {
type: 'string',
description: 'Event creation time (ISO 8601)',
},
resource: {
type: 'json',
description: 'Event resource payload',
},
resourceContainers: {
type: 'json',
description: 'Resource container references (project, collection, etc.)',
},
message: {
type: 'json',
description: 'Short message object',
},
detailedMessage: {
type: 'json',
description: 'Detailed message object',
},
}
}
export function formatBuildCompleteInput(body: Record<string, unknown>): Record<string, unknown> {
const resource = (body.resource ?? {}) as Record<string, unknown>
const definition = (resource.definition ?? {}) as Record<string, unknown>
const project = (resource.project ?? {}) as Record<string, unknown>
const requestedFor = (resource.requestedFor ?? {}) as Record<string, unknown>
const sourceBranch = (resource.sourceBranch as string) ?? ''
return {
buildId: Number(resource.id ?? 0),
buildNumber: (resource.buildNumber as string) ?? '',
result: (resource.result as string) ?? '',
pipelineId: Number(definition.id ?? 0),
pipelineName: (definition.name as string) ?? '',
projectName: (project.name as string) ?? '',
branch: sourceBranch.replace(/^refs\/heads\//, ''),
commitSha: (resource.sourceVersion as string) ?? '',
triggeredBy: (requestedFor.displayName as string) ?? null,
triggeredByEmail: (requestedFor.uniqueName as string) ?? null,
startTime: (resource.startTime as string) ?? '',
finishTime: (resource.finishTime as string) ?? '',
buildUrl: (resource.url as string) ?? '',
}
}
export function formatWorkItemCreatedInput(body: Record<string, unknown>): Record<string, unknown> {
const resource = (body.resource ?? {}) as Record<string, unknown>
const fields = (resource.fields ?? {}) as Record<string, unknown>
return {
workItemId: Number(resource.id ?? 0),
workItemType: (fields['System.WorkItemType'] as string) ?? '',
title: (fields['System.Title'] as string) ?? '',
state: (fields['System.State'] as string) ?? '',
createdBy:
(fields['System.CreatedBy'] as { displayName?: string } | undefined)?.displayName ?? null,
assignedTo:
(fields['System.AssignedTo'] as { displayName?: string } | undefined)?.displayName ?? null,
priority: Number(fields['Microsoft.VSTS.Common.Priority'] ?? 0),
areaPath: (fields['System.AreaPath'] as string) ?? '',
iterationPath: (fields['System.IterationPath'] as string) ?? '',
description: (fields['System.Description'] as string) ?? null,
projectName: (fields['System.TeamProject'] as string) ?? '',
workItemUrl: (resource.url as string) ?? '',
}
}
export function formatWebhookEnvelopeInput(body: Record<string, unknown>): Record<string, unknown> {
return {
eventType: (body.eventType as string) ?? '',
notificationId: Number(body.notificationId ?? 0),
subscriptionId: (body.subscriptionId as string) ?? '',
publisherId: (body.publisherId as string) ?? '',
createdDate: (body.createdDate as string) ?? '',
resource: body.resource ?? null,
resourceContainers: body.resourceContainers ?? null,
message: body.message ?? null,
detailedMessage: body.detailedMessage ?? null,
}
}
+37
View File
@@ -0,0 +1,37 @@
import { AzureIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
azureDevOpsTriggerOptions,
buildWebhookOutputs,
webhookSetupInstructions,
} from '@/triggers/azure_devops/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Azure DevOps generic webhook trigger.
* Event filtering is determined by which events you enable on the service hook subscription.
*/
export const azureDevOpsWebhookTrigger: TriggerConfig = {
id: 'azure_devops_webhook',
name: 'Azure DevOps Webhook (All Service Hook Events)',
provider: 'azure_devops',
description:
'Trigger on whichever service hook event types you configure in Azure DevOps. Sim does not filter deliveries for this trigger.',
version: '1.0.0',
icon: AzureIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'azure_devops_webhook',
triggerOptions: azureDevOpsTriggerOptions,
setupInstructions: webhookSetupInstructions,
}),
outputs: buildWebhookOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,32 @@
import { AzureIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
azureDevOpsTriggerOptions,
buildWorkItemCreatedOutputs,
workItemCreatedSetupInstructions,
} from '@/triggers/azure_devops/utils'
import type { TriggerConfig } from '@/triggers/types'
export const azureDevOpsWorkItemCreatedTrigger: TriggerConfig = {
id: 'azure_devops_work_item_created',
name: 'Azure DevOps Work Item Created',
provider: 'azure_devops',
description: 'Trigger workflow when a work item is created in Azure DevOps',
version: '1.0.0',
icon: AzureIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'azure_devops_work_item_created',
triggerOptions: azureDevOpsTriggerOptions,
setupInstructions: workItemCreatedSetupInstructions,
}),
outputs: buildWorkItemCreatedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildCancelledOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingCancelledTrigger: TriggerConfig = {
id: 'calcom_booking_cancelled',
name: 'CalCom Booking Cancelled',
provider: 'calcom',
description: 'Trigger workflow when a booking is cancelled in Cal.com',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_cancelled',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('cancelled'),
extraFields: [calcomWebhookSecretField('calcom_booking_cancelled')],
}),
outputs: buildCancelledOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
@@ -0,0 +1,36 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBookingOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingCreatedTrigger: TriggerConfig = {
id: 'calcom_booking_created',
name: 'CalCom Booking Created',
provider: 'calcom',
description: 'Trigger workflow when a new booking is created in Cal.com',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_created',
triggerOptions: calcomTriggerOptions,
includeDropdown: true,
setupInstructions: calcomSetupInstructions('created'),
extraFields: [calcomWebhookSecretField('calcom_booking_created')],
}),
outputs: buildBookingOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
+35
View File
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildPaidOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingPaidTrigger: TriggerConfig = {
id: 'calcom_booking_paid',
name: 'CalCom Booking Paid',
provider: 'calcom',
description: 'Trigger workflow when payment is completed for a paid booking',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_paid',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('paid'),
extraFields: [calcomWebhookSecretField('calcom_booking_paid')],
}),
outputs: buildPaidOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildRejectedOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingRejectedTrigger: TriggerConfig = {
id: 'calcom_booking_rejected',
name: 'CalCom Booking Rejected',
provider: 'calcom',
description: 'Trigger workflow when a booking request is rejected by the host',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_rejected',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('rejected'),
extraFields: [calcomWebhookSecretField('calcom_booking_rejected')],
}),
outputs: buildRejectedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildRequestedOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingRequestedTrigger: TriggerConfig = {
id: 'calcom_booking_requested',
name: 'CalCom Booking Requested',
provider: 'calcom',
description: 'Trigger workflow when a booking request is submitted (pending confirmation)',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_requested',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('requested'),
extraFields: [calcomWebhookSecretField('calcom_booking_requested')],
}),
outputs: buildRequestedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildRescheduledOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomBookingRescheduledTrigger: TriggerConfig = {
id: 'calcom_booking_rescheduled',
name: 'CalCom Booking Rescheduled',
provider: 'calcom',
description: 'Trigger workflow when a booking is rescheduled in Cal.com',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_booking_rescheduled',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('rescheduled'),
extraFields: [calcomWebhookSecretField('calcom_booking_rescheduled')],
}),
outputs: buildRescheduledOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
+9
View File
@@ -0,0 +1,9 @@
export { calcomBookingCancelledTrigger } from './booking_cancelled'
export { calcomBookingCreatedTrigger } from './booking_created'
export { calcomBookingPaidTrigger } from './booking_paid'
export { calcomBookingRejectedTrigger } from './booking_rejected'
export { calcomBookingRequestedTrigger } from './booking_requested'
export { calcomBookingRescheduledTrigger } from './booking_rescheduled'
export { calcomMeetingEndedTrigger } from './meeting_ended'
export { calcomRecordingReadyTrigger } from './recording_ready'
export { calcomWebhookTrigger } from './webhook'
+35
View File
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildMeetingEndedOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomMeetingEndedTrigger: TriggerConfig = {
id: 'calcom_meeting_ended',
name: 'CalCom Meeting Ended',
provider: 'calcom',
description: 'Trigger workflow when a Cal.com meeting ends',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_meeting_ended',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('meeting_ended'),
extraFields: [calcomWebhookSecretField('calcom_meeting_ended')],
}),
outputs: buildMeetingEndedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
@@ -0,0 +1,35 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildRecordingReadyOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calcomRecordingReadyTrigger: TriggerConfig = {
id: 'calcom_recording_ready',
name: 'CalCom Recording Ready',
provider: 'calcom',
description: 'Trigger workflow when a meeting recording is ready for download',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_recording_ready',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('recording_ready'),
extraFields: [calcomWebhookSecretField('calcom_recording_ready')],
}),
outputs: buildRecordingReadyOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
+664
View File
@@ -0,0 +1,664 @@
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Shared output property definitions for Cal.com trigger payloads.
*/
/**
* Organizer output definition with destructured properties
*/
const ORGANIZER_OUTPUT: TriggerOutput = {
type: 'object',
description: 'Organizer details',
properties: {
id: { type: 'number', description: 'Organizer user ID' },
name: { type: 'string', description: 'Organizer name' },
email: { type: 'string', description: 'Organizer email' },
username: { type: 'string', description: 'Organizer username' },
timeZone: { type: 'string', description: 'Organizer timezone' },
},
}
/**
* Attendees array output definition with destructured items
*/
const ATTENDEES_TRIGGER_OUTPUT: TriggerOutput = {
type: 'array',
description: 'List of attendees',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Attendee name' },
email: { type: 'string', description: 'Attendee email' },
timeZone: { type: 'string', description: 'Attendee timezone' },
language: { type: 'string', description: 'Attendee language preference' },
},
},
}
export const calcomTriggerOptions = [
{ label: 'Booking Created', id: 'calcom_booking_created' },
{ label: 'Booking Cancelled', id: 'calcom_booking_cancelled' },
{ label: 'Booking Rescheduled', id: 'calcom_booking_rescheduled' },
{ label: 'Booking Requested', id: 'calcom_booking_requested' },
{ label: 'Booking Rejected', id: 'calcom_booking_rejected' },
{ label: 'Booking Paid', id: 'calcom_booking_paid' },
{ label: 'Meeting Ended', id: 'calcom_meeting_ended' },
{ label: 'Recording Ready', id: 'calcom_recording_ready' },
{ label: 'Generic Webhook (All Events)', id: 'calcom_webhook' },
]
/**
* Creates the webhook secret field subBlock for a CalCom trigger
*/
export function calcomWebhookSecretField(triggerId: string): SubBlockConfig {
return {
id: 'webhookSecret',
title: 'Webhook Secret',
type: 'short-input',
placeholder: 'Enter the same secret you configured in Cal.com',
description: 'Used to verify webhook requests via X-Cal-Signature-256 header.',
password: true,
required: false,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: triggerId,
},
}
}
/**
* Event type configuration for setup instructions
*/
type CalcomEventType =
| 'created'
| 'cancelled'
| 'rescheduled'
| 'requested'
| 'rejected'
| 'paid'
| 'meeting_ended'
| 'recording_ready'
| 'generic'
/**
* Generates setup instructions HTML for CalCom triggers
*/
export function calcomSetupInstructions(eventType: CalcomEventType): string {
const eventDescriptions: Record<CalcomEventType, string> = {
created: 'This webhook triggers when a new booking is created.',
cancelled: 'This webhook triggers when a booking is cancelled.',
rescheduled: 'This webhook triggers when a booking is rescheduled.',
requested:
'This webhook triggers when a booking request is submitted (for event types requiring confirmation).',
rejected: 'This webhook triggers when a booking request is rejected by the host.',
paid: 'This webhook triggers when payment is completed for a paid booking.',
meeting_ended: 'This webhook triggers when a meeting ends.',
recording_ready: 'This webhook triggers when a meeting recording is ready for download.',
generic: 'This webhook can receive any Cal.com event type you configure.',
}
const eventNames: Record<CalcomEventType, string> = {
created: 'BOOKING_CREATED',
cancelled: 'BOOKING_CANCELLED',
rescheduled: 'BOOKING_RESCHEDULED',
requested: 'BOOKING_REQUESTED',
rejected: 'BOOKING_REJECTED',
paid: 'BOOKING_PAID',
meeting_ended: 'MEETING_ENDED',
recording_ready: 'RECORDING_READY',
generic: 'your desired event type(s)',
}
return [
'Copy the webhook URL above.',
'Go to your <a href="https://app.cal.com/settings/developer/webhooks" target="_blank" rel="noopener noreferrer">Cal.com Webhook Settings</a>.',
'Click "New Webhook" and paste the URL.',
`Select the <strong>${eventNames[eventType]}</strong> event trigger.`,
eventDescriptions[eventType],
'If you add a secret key in Cal.com, enter the same secret in the <strong>Webhook Secret</strong> field above to verify webhook authenticity.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Builds common booking outputs for CalCom triggers
*/
export function buildBookingOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Booking start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Booking end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status',
},
location: {
type: 'string',
description: 'Meeting location or URL',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
responses: {
type: 'json',
description:
'Booking form responses (dynamic - fields depend on your event type configuration)',
},
metadata: {
type: 'json',
description:
'Custom metadata attached to the booking (dynamic - user-defined key-value pairs)',
},
videoCallData: {
type: 'json',
description: 'Video call details (structure varies by provider)',
},
},
} as any
}
/**
* Builds outputs specific to cancelled bookings
*/
export function buildCancelledOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Booking start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Booking end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status',
},
location: {
type: 'string',
description: 'Meeting location or URL',
},
cancellationReason: {
type: 'string',
description: 'Reason for cancellation',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
responses: {
type: 'json',
description: 'Booking form responses',
},
metadata: {
type: 'json',
description: 'Custom metadata attached to the booking',
},
},
} as any
}
/**
* Builds outputs specific to rescheduled bookings
*/
export function buildRescheduledOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'New booking start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'New booking end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status',
},
location: {
type: 'string',
description: 'Meeting location or URL',
},
rescheduleId: {
type: 'number',
description: 'Previous booking ID',
},
rescheduleUid: {
type: 'string',
description: 'Previous booking UID',
},
rescheduleStartTime: {
type: 'string',
description: 'Original start time (ISO 8601)',
},
rescheduleEndTime: {
type: 'string',
description: 'Original end time (ISO 8601)',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
responses: {
type: 'json',
description: 'Booking form responses',
},
metadata: {
type: 'json',
description: 'Custom metadata attached to the booking',
},
},
} as any
}
/**
* Builds outputs for booking requested events (pending confirmation)
*/
export function buildRequestedOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (BOOKING_REQUESTED)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Requested start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Requested end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status (pending)',
},
location: {
type: 'string',
description: 'Meeting location or URL',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
responses: {
type: 'json',
description: 'Booking form responses',
},
metadata: {
type: 'json',
description: 'Custom metadata attached to the booking',
},
},
} as any
}
/**
* Builds outputs for booking rejected events
*/
export function buildRejectedOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (BOOKING_REJECTED)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Requested start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Requested end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status (rejected)',
},
rejectionReason: {
type: 'string',
description: 'Reason for rejection provided by host',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
metadata: {
type: 'json',
description: 'Custom metadata attached to the booking',
},
},
} as any
}
/**
* Builds outputs for booking paid events
*/
export function buildPaidOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (BOOKING_PAID)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Booking title',
},
description: {
type: 'string',
description: 'Booking description',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Booking start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Booking end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
status: {
type: 'string',
description: 'Booking status',
},
location: {
type: 'string',
description: 'Meeting location or URL',
},
payment: {
type: 'object',
description: 'Payment details',
properties: {
id: { type: 'string', description: 'Payment ID' },
amount: { type: 'number', description: 'Payment amount' },
currency: { type: 'string', description: 'Payment currency' },
success: { type: 'boolean', description: 'Whether payment succeeded' },
},
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
metadata: {
type: 'json',
description: 'Custom metadata attached to the booking',
},
},
} as any
}
/**
* Builds outputs for meeting ended events
*/
export function buildMeetingEndedOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (MEETING_ENDED)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Meeting title',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Meeting start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Meeting end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
duration: {
type: 'number',
description: 'Actual meeting duration in minutes',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
videoCallData: {
type: 'json',
description: 'Video call details',
},
},
} as any
}
/**
* Builds outputs for recording ready events
*/
export function buildRecordingReadyOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (RECORDING_READY)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
title: {
type: 'string',
description: 'Meeting title',
},
eventTypeId: {
type: 'number',
description: 'Event type ID',
},
startTime: {
type: 'string',
description: 'Meeting start time (ISO 8601)',
},
endTime: {
type: 'string',
description: 'Meeting end time (ISO 8601)',
},
uid: {
type: 'string',
description: 'Unique booking identifier',
},
bookingId: {
type: 'number',
description: 'Numeric booking ID',
},
recordingUrl: {
type: 'string',
description: 'URL to download the recording',
},
transcription: {
type: 'string',
description: 'Meeting transcription text (if available)',
},
organizer: ORGANIZER_OUTPUT,
attendees: ATTENDEES_TRIGGER_OUTPUT,
},
} as any
}
/**
* Builds outputs for generic webhook (any event type)
*/
export function buildGenericOutputs(): Record<string, TriggerOutput> {
return {
triggerEvent: {
type: 'string',
description: 'The webhook event type (e.g., BOOKING_CREATED, MEETING_ENDED)',
},
createdAt: {
type: 'string',
description: 'When the webhook event was created (ISO 8601)',
},
payload: {
type: 'json',
description: 'Complete webhook payload (structure varies by event type)',
},
}
}
+40
View File
@@ -0,0 +1,40 @@
import { CalComIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildGenericOutputs,
calcomSetupInstructions,
calcomTriggerOptions,
calcomWebhookSecretField,
} from '@/triggers/calcom/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Generic Cal.com webhook trigger that accepts any event type.
* Use this when you need to handle events not covered by specific triggers,
* or when you want to receive multiple event types on the same webhook.
*/
export const calcomWebhookTrigger: TriggerConfig = {
id: 'calcom_webhook',
name: 'CalCom Webhook (All Events)',
provider: 'calcom',
description: 'Trigger workflow on any Cal.com webhook event (configure event types in Cal.com)',
version: '1.0.0',
icon: CalComIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'calcom_webhook',
triggerOptions: calcomTriggerOptions,
setupInstructions: calcomSetupInstructions('generic'),
extraFields: [calcomWebhookSecretField('calcom_webhook')],
}),
outputs: buildGenericOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Cal-Signature-256': '<hmac-sha256-hex>',
},
},
}
+4
View File
@@ -0,0 +1,4 @@
export { calendlyInviteeCanceledTrigger } from './invitee_canceled'
export { calendlyInviteeCreatedTrigger } from './invitee_created'
export { calendlyRoutingFormSubmittedTrigger } from './routing_form_submitted'
export { calendlyWebhookTrigger } from './webhook'
@@ -0,0 +1,76 @@
import { CalendlyIcon } from '@/components/icons'
import { buildInviteeOutputs } from '@/triggers/calendly/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calendlyInviteeCanceledTrigger: TriggerConfig = {
id: 'calendly_invitee_canceled',
name: 'Calendly Invitee Canceled',
provider: 'calendly',
description: 'Trigger workflow when someone cancels a scheduled event on Calendly',
version: '1.0.0',
icon: CalendlyIcon,
subBlocks: [
{
id: 'apiKey',
title: 'Personal Access Token',
type: 'short-input',
placeholder: 'Enter your Calendly personal access token',
password: true,
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_canceled',
},
},
{
id: 'organization',
title: 'Organization URI',
type: 'short-input',
placeholder: 'https://api.calendly.com/organizations/XXXXXX',
description:
'Organization URI for the webhook subscription. Get this from "Get Current User" operation.',
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_canceled',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: [
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
'The webhook will be automatically created in Calendly when you deploy the workflow.',
'This webhook triggers when an invitee cancels an event. The payload includes cancellation details and reason.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join(''),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_canceled',
},
},
],
outputs: buildInviteeOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Calendly-Webhook-Signature': 'v1,signature...',
'User-Agent': 'Calendly-Webhook',
},
},
}
@@ -0,0 +1,85 @@
import { CalendlyIcon } from '@/components/icons'
import { buildInviteeOutputs, calendlyTriggerOptions } from '@/triggers/calendly/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calendlyInviteeCreatedTrigger: TriggerConfig = {
id: 'calendly_invitee_created',
name: 'Calendly Invitee Created',
provider: 'calendly',
description: 'Trigger workflow when someone schedules a new event on Calendly',
version: '1.0.0',
icon: CalendlyIcon,
subBlocks: [
{
id: 'selectedTriggerId',
title: 'Trigger Type',
type: 'dropdown',
mode: 'trigger',
options: calendlyTriggerOptions,
value: () => 'calendly_invitee_created',
required: true,
},
{
id: 'apiKey',
title: 'Personal Access Token',
type: 'short-input',
placeholder: 'Enter your Calendly personal access token',
password: true,
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_created',
},
},
{
id: 'organization',
title: 'Organization URI',
type: 'short-input',
placeholder: 'https://api.calendly.com/organizations/XXXXXX',
description:
'Organization URI for the webhook subscription. Get this from "Get Current User" operation.',
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_created',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: [
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
'The webhook will be automatically created in Calendly when you deploy the workflow.',
'This webhook triggers when an invitee schedules a new event. Rescheduling triggers both cancellation and creation events.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join(''),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_invitee_created',
},
},
],
outputs: buildInviteeOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Calendly-Webhook-Signature': 'v1,signature...',
'User-Agent': 'Calendly-Webhook',
},
},
}
@@ -0,0 +1,76 @@
import { CalendlyIcon } from '@/components/icons'
import { buildRoutingFormOutputs } from '@/triggers/calendly/utils'
import type { TriggerConfig } from '@/triggers/types'
export const calendlyRoutingFormSubmittedTrigger: TriggerConfig = {
id: 'calendly_routing_form_submitted',
name: 'Calendly Routing Form Submitted',
provider: 'calendly',
description: 'Trigger workflow when someone submits a Calendly routing form',
version: '1.0.0',
icon: CalendlyIcon,
subBlocks: [
{
id: 'apiKey',
title: 'Personal Access Token',
type: 'short-input',
placeholder: 'Enter your Calendly personal access token',
password: true,
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_routing_form_submitted',
},
},
{
id: 'organization',
title: 'Organization URI',
type: 'short-input',
placeholder: 'https://api.calendly.com/organizations/XXXXXX',
description:
'Organization URI for the webhook subscription. Get this from "Get Current User" operation.',
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_routing_form_submitted',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: [
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
'The webhook will be automatically created in Calendly when you deploy the workflow.',
'This webhook triggers when someone submits a routing form, regardless of whether they book an event.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join(''),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_routing_form_submitted',
},
},
],
outputs: buildRoutingFormOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Calendly-Webhook-Signature': 'v1,signature...',
'User-Agent': 'Calendly-Webhook',
},
},
}
+341
View File
@@ -0,0 +1,341 @@
import type { TriggerOutput } from '@/triggers/types'
/**
* Shared trigger dropdown options for all Calendly triggers
*/
export const calendlyTriggerOptions = [
{ label: 'Invitee Created', id: 'calendly_invitee_created' },
{ label: 'Invitee Canceled', id: 'calendly_invitee_canceled' },
{ label: 'Routing Form Submitted', id: 'calendly_routing_form_submitted' },
{ label: 'General Webhook (All Events)', id: 'calendly_webhook' },
]
/**
* Generate setup instructions for a specific Calendly event type
*/
export function calendlySetupInstructions(eventType: string, additionalNotes?: string): string {
const instructions = [
'<strong>Note:</strong> Webhooks require a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
'<strong>Important:</strong> Calendly does not provide a UI for creating webhooks. You must create them programmatically using the API. See the <a href="https://developer.calendly.com/api-docs" target="_blank" rel="noopener noreferrer">Calendly Developer documentation</a> for details.',
'Get your Calendly <strong>Personal Access Token</strong> from the Calendly dashboard under <strong>Integrations > API & Webhooks</strong>.',
'In your workflow, add a Calendly block and select the <strong>"Create Webhook"</strong> operation.',
'Enter your Personal Access Token in the Calendly block.',
'Copy the <strong>Webhook URL</strong> shown above and paste it into the webhook URL field in the Create Webhook operation.',
`Select the event types to monitor. For this trigger, select <strong>${eventType}</strong>.`,
'Set the scope to <strong>Organization</strong> or <strong>User</strong> as needed (routing form submissions require organization scope).',
'Run the workflow to create the webhook subscription. You can use the "List Webhooks" operation to verify it was created.',
]
if (additionalNotes) {
instructions.push(additionalNotes)
}
return instructions
.map(
(instruction, index) =>
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
)
.join('')
}
/**
* Shared tracking output schema
*/
export const trackingOutputs = {
utm_campaign: {
type: 'string',
description: 'UTM campaign parameter',
},
utm_source: {
type: 'string',
description: 'UTM source parameter',
},
utm_medium: {
type: 'string',
description: 'UTM medium parameter',
},
utm_content: {
type: 'string',
description: 'UTM content parameter',
},
utm_term: {
type: 'string',
description: 'UTM term parameter',
},
salesforce_uuid: {
type: 'string',
description: 'Salesforce UUID',
},
} as const
/**
* Shared questions and answers output schema
*/
export const questionsAndAnswersOutputs = {
type: 'array',
description: 'Questions and answers from the booking form',
items: {
question: {
type: 'string',
description: 'Question text',
},
answer: {
type: 'string',
description: 'Answer text',
},
},
} as const
/**
* Build output schema for invitee events
*/
export function buildInviteeOutputs(): Record<string, TriggerOutput> {
return {
event: {
type: 'string',
description: 'Event type (invitee.created or invitee.canceled)',
},
created_at: {
type: 'string',
description: 'Webhook event creation timestamp',
},
created_by: {
type: 'string',
description: 'URI of the Calendly user who created this webhook',
},
payload: {
uri: {
type: 'string',
description: 'Invitee URI',
},
email: {
type: 'string',
description: 'Invitee email address',
},
name: {
type: 'string',
description: 'Invitee full name',
},
first_name: {
type: 'string',
description: 'Invitee first name',
},
last_name: {
type: 'string',
description: 'Invitee last name',
},
status: {
type: 'string',
description: 'Invitee status (active or canceled)',
},
timezone: {
type: 'string',
description: 'Invitee timezone',
},
event: {
type: 'string',
description: 'Scheduled event URI',
},
questions_and_answers: questionsAndAnswersOutputs,
tracking: trackingOutputs,
text_reminder_number: {
type: 'string',
description: 'Phone number for text reminders',
},
rescheduled: {
type: 'boolean',
description: 'Whether this invitee rescheduled',
},
old_invitee: {
type: 'string',
description: 'URI of the old invitee (if rescheduled)',
},
new_invitee: {
type: 'string',
description: 'URI of the new invitee (if rescheduled)',
},
cancel_url: {
type: 'string',
description: 'URL to cancel the event',
},
reschedule_url: {
type: 'string',
description: 'URL to reschedule the event',
},
created_at: {
type: 'string',
description: 'Invitee creation timestamp',
},
updated_at: {
type: 'string',
description: 'Invitee last update timestamp',
},
canceled: {
type: 'boolean',
description: 'Whether the event was canceled',
},
cancellation: {
type: 'object',
description: 'Cancellation details',
properties: {
canceled_by: {
type: 'string',
description: 'Who canceled the event',
},
reason: {
type: 'string',
description: 'Cancellation reason',
},
},
},
payment: {
type: 'object',
description: 'Payment details',
properties: {
id: {
type: 'string',
description: 'Payment ID',
},
provider: {
type: 'string',
description: 'Payment provider',
},
amount: {
type: 'number',
description: 'Payment amount',
},
currency: {
type: 'string',
description: 'Payment currency',
},
terms: {
type: 'string',
description: 'Payment terms',
},
successful: {
type: 'boolean',
description: 'Whether payment was successful',
},
},
},
no_show: {
type: 'object',
description: 'No-show details',
properties: {
created_at: {
type: 'string',
description: 'No-show marked timestamp',
},
},
},
reconfirmation: {
type: 'object',
description: 'Reconfirmation details',
properties: {
created_at: {
type: 'string',
description: 'Reconfirmation timestamp',
},
confirmed_at: {
type: 'string',
description: 'Confirmation timestamp',
},
},
},
},
} as any
}
/**
* Build output schema for routing form submission events
*/
export function buildRoutingFormOutputs(): Record<string, TriggerOutput> {
return {
event: {
type: 'string',
description: 'Event type (routing_form_submission.created)',
},
created_at: {
type: 'string',
description: 'Webhook event creation timestamp',
},
created_by: {
type: 'string',
description: 'URI of the Calendly user who created this webhook',
},
payload: {
uri: {
type: 'string',
description: 'Routing form submission URI',
},
routing_form: {
type: 'string',
description: 'Routing form URI',
},
submitter: {
type: 'object',
description: 'Submitter details',
properties: {
uri: {
type: 'string',
description: 'Submitter URI',
},
email: {
type: 'string',
description: 'Submitter email address',
},
name: {
type: 'string',
description: 'Submitter full name',
},
},
},
submitter_type: {
type: 'string',
description: 'Type of submitter',
},
questions_and_answers: questionsAndAnswersOutputs,
tracking: trackingOutputs,
result: {
type: 'object',
description: 'Routing result details',
properties: {
type: {
type: 'string',
description: 'Result type (event_type, custom_message, or external_url)',
},
value: {
type: 'string',
description: 'Result value (event type URI, message, or URL)',
},
},
},
created_at: {
type: 'string',
description: 'Submission creation timestamp',
},
updated_at: {
type: 'string',
description: 'Submission last update timestamp',
},
},
} as any
}
/**
* Check if a Calendly event matches the expected trigger configuration
*/
export function isCalendlyEventMatch(triggerId: string, eventType: string): boolean {
const eventMap: Record<string, string> = {
calendly_invitee_created: 'invitee.created',
calendly_invitee_canceled: 'invitee.canceled',
calendly_routing_form_submitted: 'routing_form_submission.created',
}
const expectedEvent = eventMap[triggerId]
if (!expectedEvent) {
return true // Unknown trigger or general webhook, allow through
}
return expectedEvent === eventType
}
+93
View File
@@ -0,0 +1,93 @@
import { CalendlyIcon } from '@/components/icons'
import type { TriggerConfig } from '@/triggers/types'
export const calendlyWebhookTrigger: TriggerConfig = {
id: 'calendly_webhook',
name: 'Calendly Webhook',
provider: 'calendly',
description: 'Trigger workflow from any Calendly webhook event',
version: '1.0.0',
icon: CalendlyIcon,
subBlocks: [
{
id: 'apiKey',
title: 'Personal Access Token',
type: 'short-input',
placeholder: 'Enter your Calendly personal access token',
password: true,
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_webhook',
},
},
{
id: 'organization',
title: 'Organization URI',
type: 'short-input',
placeholder: 'https://api.calendly.com/organizations/XXXXXX',
description:
'Organization URI for the webhook subscription. Get this from "Get Current User" operation.',
required: true,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_webhook',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: [
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
'The webhook will be automatically created in Calendly when you deploy the workflow.',
'This webhook subscribes to all Calendly events (invitee created, invitee canceled, and routing form submitted). Use the <code>event</code> field in the payload to determine the event type.',
]
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join(''),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'calendly_webhook',
},
},
],
outputs: {
event: {
type: 'string',
description:
'Event type (invitee.created, invitee.canceled, or routing_form_submission.created)',
},
created_at: {
type: 'string',
description: 'Webhook event creation timestamp',
},
created_by: {
type: 'string',
description: 'URI of the Calendly user who created this webhook',
},
payload: {
type: 'object',
description: 'Complete event payload (structure varies by event type)',
},
},
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Calendly-Webhook-Signature': 'v1,signature...',
'User-Agent': 'Calendly-Webhook',
},
},
}
+3
View File
@@ -0,0 +1,3 @@
export { circlebackMeetingCompletedTrigger } from './meeting_completed'
export { circlebackMeetingNotesTrigger } from './meeting_notes'
export { circlebackWebhookTrigger } from './webhook'
@@ -0,0 +1,64 @@
import { CirclebackIcon } from '@/components/icons'
import type { TriggerConfig } from '@/triggers/types'
import { buildMeetingOutputs, circlebackSetupInstructions } from './utils'
export const circlebackMeetingCompletedTrigger: TriggerConfig = {
id: 'circleback_meeting_completed',
name: 'Circleback Meeting Completed',
provider: 'circleback',
description: 'Trigger workflow when a meeting is processed and ready in Circleback',
version: '1.0.0',
icon: CirclebackIcon,
subBlocks: [
{
id: 'webhookUrlDisplay',
title: 'Webhook URL',
type: 'short-input',
readOnly: true,
showCopyButton: true,
useWebhookUrl: true,
placeholder: 'Webhook URL will be generated',
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_completed',
},
},
{
id: 'webhookSecret',
title: 'Signing Secret',
type: 'short-input',
placeholder: 'Paste signing secret from Circleback (optional)',
description: 'Validates that webhook deliveries originate from Circleback using HMAC-SHA256.',
password: true,
required: false,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_completed',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: circlebackSetupInstructions('All meeting data'),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_completed',
},
},
],
outputs: buildMeetingOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,64 @@
import { CirclebackIcon } from '@/components/icons'
import type { TriggerConfig } from '@/triggers/types'
import { buildMeetingOutputs, circlebackSetupInstructions } from './utils'
export const circlebackMeetingNotesTrigger: TriggerConfig = {
id: 'circleback_meeting_notes',
name: 'Circleback Meeting Notes Ready',
provider: 'circleback',
description: 'Trigger workflow when meeting notes and action items are ready',
version: '1.0.0',
icon: CirclebackIcon,
subBlocks: [
{
id: 'webhookUrlDisplay',
title: 'Webhook URL',
type: 'short-input',
readOnly: true,
showCopyButton: true,
useWebhookUrl: true,
placeholder: 'Webhook URL will be generated',
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_notes',
},
},
{
id: 'webhookSecret',
title: 'Signing Secret',
type: 'short-input',
placeholder: 'Paste signing secret from Circleback (optional)',
description: 'Validates that webhook deliveries originate from Circleback using HMAC-SHA256.',
password: true,
required: false,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_notes',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: circlebackSetupInstructions('Meeting notes and action items'),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_meeting_notes',
},
},
],
outputs: buildMeetingOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+137
View File
@@ -0,0 +1,137 @@
import type { TriggerOutput } from '@/triggers/types'
/**
* Shared trigger dropdown options for all Circleback triggers
*/
export const circlebackTriggerOptions = [
{ label: 'General Webhook', id: 'circleback_webhook' },
{ label: 'Meeting Completed', id: 'circleback_meeting_completed' },
{ label: 'Meeting Notes Ready', id: 'circleback_meeting_notes' },
]
/**
* Generate setup instructions for a specific Circleback event type
*/
export function circlebackSetupInstructions(eventType: string): string {
const instructions = [
'<strong>Note:</strong> You need access to Circleback automations to set up webhooks. See the <a href="https://circleback.ai/docs/webhook-integration" target="_blank" rel="noopener noreferrer">Circleback webhook documentation</a> for details.',
'In Circleback, click <strong>Automations</strong> in the sidebar.',
'Create a new automation or edit an existing one.',
'Add a <strong>Send webhook request</strong> step to your automation.',
'Paste the <strong>Webhook URL</strong> from above into the Endpoint field.',
'Optionally, copy the <strong>Signing Secret</strong> from Circleback and paste it above to verify webhook signatures.',
`Toggle what to include in the request (Meeting notes, Action items, Transcript). For this trigger: <strong>${eventType}</strong>.`,
'Click <strong>"Done"</strong> and save the automation.',
]
return instructions
.map(
(instruction, index) =>
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
)
.join('')
}
/**
* Build output schema for meeting events
*/
export function buildMeetingOutputs(): Record<string, TriggerOutput> {
return {
id: {
type: 'number',
description: 'Circleback meeting ID',
},
name: {
type: 'string',
description: 'Meeting title/name',
},
url: {
type: 'string',
description: 'URL of the virtual meeting (Zoom, Google Meet, Teams, etc.)',
},
createdAt: {
type: 'string',
description: 'ISO8601 timestamp when meeting was created',
},
duration: {
type: 'number',
description: 'Meeting duration in seconds',
},
recordingUrl: {
type: 'string',
description: 'Recording URL (valid for 24 hours, if enabled)',
},
tags: {
type: 'array',
description: 'Array of tag strings',
},
icalUid: {
type: 'string',
description: 'Calendar event identifier',
},
attendees: {
type: 'array',
description: 'Array of attendee objects with name and email',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Attendee name' },
email: { type: 'string', description: 'Attendee email address' },
},
},
},
notes: {
type: 'string',
description: 'Meeting notes in Markdown format',
},
actionItems: {
type: 'array',
description: 'Array of action item objects',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Action item ID' },
title: { type: 'string', description: 'Action item title' },
description: { type: 'string', description: 'Action item description' },
assignee: {
type: 'object',
description: 'Person assigned to the action item (or null)',
properties: {
name: { type: 'string', description: 'Assignee name' },
email: { type: 'string', description: 'Assignee email' },
},
},
status: { type: 'string', description: 'Status: PENDING or DONE' },
},
},
},
transcript: {
type: 'array',
description: 'Array of transcript segments',
items: {
type: 'object',
properties: {
speaker: { type: 'string', description: 'Speaker name' },
text: { type: 'string', description: 'Transcript text' },
timestamp: { type: 'number', description: 'Timestamp in seconds' },
},
},
},
insights: {
type: 'object',
description: 'User-created insights keyed by insight name',
},
meeting: {
type: 'object',
description: 'Full meeting payload object',
properties: {
id: { type: 'number', description: 'Meeting ID' },
name: { type: 'string', description: 'Meeting name' },
url: { type: 'string', description: 'Meeting URL' },
duration: { type: 'number', description: 'Duration in seconds' },
createdAt: { type: 'string', description: 'Creation timestamp' },
recordingUrl: { type: 'string', description: 'Recording URL' },
},
},
} as any
}
+73
View File
@@ -0,0 +1,73 @@
import { CirclebackIcon } from '@/components/icons'
import type { TriggerConfig } from '@/triggers/types'
import { buildMeetingOutputs, circlebackSetupInstructions, circlebackTriggerOptions } from './utils'
export const circlebackWebhookTrigger: TriggerConfig = {
id: 'circleback_webhook',
name: 'Circleback Webhook',
provider: 'circleback',
description: 'Generic webhook trigger for all Circleback events',
version: '1.0.0',
icon: CirclebackIcon,
subBlocks: [
{
id: 'selectedTriggerId',
title: 'Trigger Type',
type: 'dropdown',
mode: 'trigger',
options: circlebackTriggerOptions,
value: () => 'circleback_webhook',
required: true,
},
{
id: 'webhookUrlDisplay',
title: 'Webhook URL',
type: 'short-input',
readOnly: true,
showCopyButton: true,
useWebhookUrl: true,
placeholder: 'Webhook URL will be generated',
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_webhook',
},
},
{
id: 'webhookSecret',
title: 'Signing Secret',
type: 'short-input',
placeholder: 'Paste signing secret from Circleback (optional)',
description: 'Validates that webhook deliveries originate from Circleback using HMAC-SHA256.',
password: true,
required: false,
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_webhook',
},
},
{
id: 'triggerInstructions',
title: 'Setup Instructions',
hideFromPreview: true,
type: 'text',
defaultValue: circlebackSetupInstructions('All events'),
mode: 'trigger',
condition: {
field: 'selectedTriggerId',
value: 'circleback_webhook',
},
},
],
outputs: buildMeetingOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+14
View File
@@ -0,0 +1,14 @@
export { clerkOrganizationCreatedTrigger } from './organization_created'
export { clerkOrganizationDeletedTrigger } from './organization_deleted'
export { clerkOrganizationMembershipCreatedTrigger } from './organization_membership_created'
export { clerkOrganizationMembershipDeletedTrigger } from './organization_membership_deleted'
export { clerkOrganizationMembershipUpdatedTrigger } from './organization_membership_updated'
export { clerkOrganizationUpdatedTrigger } from './organization_updated'
export { clerkSessionCreatedTrigger } from './session_created'
export { clerkSessionEndedTrigger } from './session_ended'
export { clerkSessionRemovedTrigger } from './session_removed'
export { clerkSessionRevokedTrigger } from './session_revoked'
export { clerkUserCreatedTrigger } from './user_created'
export { clerkUserDeletedTrigger } from './user_deleted'
export { clerkUserUpdatedTrigger } from './user_updated'
export { clerkWebhookTrigger } from './webhook'
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Created Trigger.
* Triggers when a new organization is created.
*/
export const clerkOrganizationCreatedTrigger: TriggerConfig = {
id: 'clerk_organization_created',
name: 'Clerk Organization Created',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization is created',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_created',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organization.created'),
extraFields: buildClerkExtraFields('clerk_organization_created'),
}),
outputs: buildOrganizationOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationDeletedOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Deleted Trigger.
* Triggers when an organization is deleted.
*/
export const clerkOrganizationDeletedTrigger: TriggerConfig = {
id: 'clerk_organization_deleted',
name: 'Clerk Organization Deleted',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization is deleted',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_deleted',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organization.deleted'),
extraFields: buildClerkExtraFields('clerk_organization_deleted'),
}),
outputs: buildOrganizationDeletedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationMembershipOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Membership Created Trigger.
* Triggers when a user is added as a member of an organization.
*/
export const clerkOrganizationMembershipCreatedTrigger: TriggerConfig = {
id: 'clerk_organization_membership_created',
name: 'Clerk Organization Membership Created',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization membership is created',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_membership_created',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organizationMembership.created'),
extraFields: buildClerkExtraFields('clerk_organization_membership_created'),
}),
outputs: buildOrganizationMembershipOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationMembershipDeletedOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Membership Deleted Trigger.
* Triggers when a member is removed from an organization.
*/
export const clerkOrganizationMembershipDeletedTrigger: TriggerConfig = {
id: 'clerk_organization_membership_deleted',
name: 'Clerk Organization Membership Deleted',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization membership is deleted',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_membership_deleted',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organizationMembership.deleted'),
extraFields: buildClerkExtraFields('clerk_organization_membership_deleted'),
}),
outputs: buildOrganizationMembershipDeletedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationMembershipOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Membership Updated Trigger.
* Triggers when a member's role within an organization changes.
*/
export const clerkOrganizationMembershipUpdatedTrigger: TriggerConfig = {
id: 'clerk_organization_membership_updated',
name: 'Clerk Organization Membership Updated',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization membership is updated',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_membership_updated',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organizationMembership.updated'),
extraFields: buildClerkExtraFields('clerk_organization_membership_updated'),
}),
outputs: buildOrganizationMembershipOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildOrganizationOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Organization Updated Trigger.
* Triggers when an organization's details are updated.
*/
export const clerkOrganizationUpdatedTrigger: TriggerConfig = {
id: 'clerk_organization_updated',
name: 'Clerk Organization Updated',
provider: 'clerk',
description: 'Trigger workflow when a Clerk organization is updated',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_organization_updated',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('organization.updated'),
extraFields: buildClerkExtraFields('clerk_organization_updated'),
}),
outputs: buildOrganizationOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildSessionOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Session Created Trigger.
* Triggers when a user signs in and a new session is created.
*/
export const clerkSessionCreatedTrigger: TriggerConfig = {
id: 'clerk_session_created',
name: 'Clerk Session Created',
provider: 'clerk',
description: 'Trigger workflow when a Clerk session is created',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_session_created',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('session.created'),
extraFields: buildClerkExtraFields('clerk_session_created'),
}),
outputs: buildSessionOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+38
View File
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildSessionOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Session Ended Trigger.
* Triggers when a user signs out and the session ends.
*/
export const clerkSessionEndedTrigger: TriggerConfig = {
id: 'clerk_session_ended',
name: 'Clerk Session Ended',
provider: 'clerk',
description: 'Trigger workflow when a Clerk session ends',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_session_ended',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('session.ended'),
extraFields: buildClerkExtraFields('clerk_session_ended'),
}),
outputs: buildSessionOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildSessionOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Session Removed Trigger.
* Triggers when a session is removed, e.g. because the associated user was deleted.
*/
export const clerkSessionRemovedTrigger: TriggerConfig = {
id: 'clerk_session_removed',
name: 'Clerk Session Removed',
provider: 'clerk',
description: 'Trigger workflow when a Clerk session is removed',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_session_removed',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('session.removed'),
extraFields: buildClerkExtraFields('clerk_session_removed'),
}),
outputs: buildSessionOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildSessionOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Session Revoked Trigger.
* Triggers when a session is revoked, e.g. via the Revoke Session API or Dashboard.
*/
export const clerkSessionRevokedTrigger: TriggerConfig = {
id: 'clerk_session_revoked',
name: 'Clerk Session Revoked',
provider: 'clerk',
description: 'Trigger workflow when a Clerk session is revoked',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_session_revoked',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('session.revoked'),
extraFields: buildClerkExtraFields('clerk_session_revoked'),
}),
outputs: buildSessionOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+39
View File
@@ -0,0 +1,39 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildUserOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk User Created Trigger (primary).
* Triggers when a new user registers or is created via the Dashboard or Backend API.
*/
export const clerkUserCreatedTrigger: TriggerConfig = {
id: 'clerk_user_created',
name: 'Clerk User Created',
provider: 'clerk',
description: 'Trigger workflow when a Clerk user is created',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_user_created',
triggerOptions: clerkTriggerOptions,
includeDropdown: true,
setupInstructions: clerkSetupInstructions('user.created'),
extraFields: buildClerkExtraFields('clerk_user_created'),
}),
outputs: buildUserOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+38
View File
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildUserDeletedOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk User Deleted Trigger.
* Triggers when a user deletes their account or is deleted.
*/
export const clerkUserDeletedTrigger: TriggerConfig = {
id: 'clerk_user_deleted',
name: 'Clerk User Deleted',
provider: 'clerk',
description: 'Trigger workflow when a Clerk user is deleted',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_user_deleted',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('user.deleted'),
extraFields: buildClerkExtraFields('clerk_user_deleted'),
}),
outputs: buildUserDeletedOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+38
View File
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildUserOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk User Updated Trigger.
* Triggers when a user's information is updated.
*/
export const clerkUserUpdatedTrigger: TriggerConfig = {
id: 'clerk_user_updated',
name: 'Clerk User Updated',
provider: 'clerk',
description: 'Trigger workflow when a Clerk user is updated',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_user_updated',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('user.updated'),
extraFields: buildClerkExtraFields('clerk_user_updated'),
}),
outputs: buildUserOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
+243
View File
@@ -0,0 +1,243 @@
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Maps Sim Clerk trigger IDs to their Clerk webhook event type.
* Kept in sync with `matchEvent` in the `clerk` webhook provider.
* Event-type strings are the canonical Clerk webhook event names
* (see https://clerk.com/docs/guides/development/webhooks/overview).
*/
export const CLERK_TRIGGER_TO_EVENT_TYPE: Record<string, string> = {
clerk_user_created: 'user.created',
clerk_user_updated: 'user.updated',
clerk_user_deleted: 'user.deleted',
clerk_session_created: 'session.created',
clerk_session_ended: 'session.ended',
clerk_session_removed: 'session.removed',
clerk_session_revoked: 'session.revoked',
clerk_organization_created: 'organization.created',
clerk_organization_updated: 'organization.updated',
clerk_organization_deleted: 'organization.deleted',
clerk_organization_membership_created: 'organizationMembership.created',
clerk_organization_membership_updated: 'organizationMembership.updated',
clerk_organization_membership_deleted: 'organizationMembership.deleted',
}
/**
* Shared trigger dropdown options for all Clerk triggers.
*/
export const clerkTriggerOptions = [
{ label: 'User Created', id: 'clerk_user_created' },
{ label: 'User Updated', id: 'clerk_user_updated' },
{ label: 'User Deleted', id: 'clerk_user_deleted' },
{ label: 'Session Created', id: 'clerk_session_created' },
{ label: 'Session Ended', id: 'clerk_session_ended' },
{ label: 'Session Removed', id: 'clerk_session_removed' },
{ label: 'Session Revoked', id: 'clerk_session_revoked' },
{ label: 'Organization Created', id: 'clerk_organization_created' },
{ label: 'Organization Updated', id: 'clerk_organization_updated' },
{ label: 'Organization Deleted', id: 'clerk_organization_deleted' },
{ label: 'Organization Membership Created', id: 'clerk_organization_membership_created' },
{ label: 'Organization Membership Updated', id: 'clerk_organization_membership_updated' },
{ label: 'Organization Membership Deleted', id: 'clerk_organization_membership_deleted' },
{ label: 'Generic Webhook (All Events)', id: 'clerk_webhook' },
]
/**
* Generate setup instructions for a specific Clerk webhook event type.
* Clerk webhooks are configured manually in the Clerk Dashboard, and the
* Signing Secret must be pasted into the trigger configuration.
*/
export function clerkSetupInstructions(eventType: string): string {
const instructions = [
'Copy the <strong>Webhook URL</strong> above.',
'In the <a href="https://dashboard.clerk.com" target="_blank" rel="noopener noreferrer">Clerk Dashboard</a>, go to <strong>Configure > Webhooks</strong> and click <strong>Add Endpoint</strong>.',
'Paste the <strong>Webhook URL</strong> into the <strong>Endpoint URL</strong> field.',
`Under <strong>Subscribe to events</strong>, select the <strong>${eventType}</strong> event, then click <strong>Create</strong>.`,
'Open the endpoint you just created and copy its <strong>Signing Secret</strong> (starts with <code>whsec_</code>).',
'Paste the <strong>Signing Secret</strong> into the field below.',
'Click <strong>"Save Configuration"</strong> above to activate your trigger.',
]
return instructions
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Build Clerk-specific extra fields.
* Includes the Svix Signing Secret used to verify incoming webhook signatures.
* Use with the generic buildTriggerSubBlocks from @/triggers.
*/
export function buildClerkExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'signingSecret',
title: 'Signing Secret',
type: 'short-input',
placeholder: 'whsec_...',
description: 'Copy this from your Clerk webhook endpoint to verify event signatures.',
password: true,
paramVisibility: 'user-only',
required: true,
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
/**
* Fields common to every Clerk webhook event payload
* (see https://clerk.com/docs/guides/development/webhooks/overview).
*/
const commonEventOutputs: Record<string, TriggerOutput> = {
type: { type: 'string', description: 'Event type (e.g., user.created, session.created)' },
object: { type: 'string', description: 'Always "event"' },
timestamp: { type: 'number', description: 'Timestamp in milliseconds when the event occurred' },
instance_id: { type: 'string', description: 'Identifier of your Clerk instance' },
data: {
type: 'json',
description: 'Raw event `data` object (shape varies by event type)',
},
}
/**
* Build outputs for `user.created` and `user.updated` events.
* The `data` object is the Clerk User object.
*/
export function buildUserOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
userId: { type: 'string', description: 'Clerk user ID (data.id)' },
firstName: { type: 'string', description: "User's first name" },
lastName: { type: 'string', description: "User's last name" },
username: { type: 'string', description: "User's username" },
imageUrl: { type: 'string', description: 'Profile image URL' },
primaryEmailAddressId: { type: 'string', description: 'Primary email address ID' },
emailAddresses: { type: 'json', description: 'Array of email address objects' },
phoneNumbers: { type: 'json', description: 'Array of phone number objects' },
externalId: { type: 'string', description: 'External system ID linked to the user' },
createdAt: { type: 'number', description: 'User creation timestamp (data.created_at)' },
updatedAt: { type: 'number', description: 'User last update timestamp (data.updated_at)' },
}
}
/**
* Build outputs for `user.deleted` events.
* The `data` object is a deleted-object marker: `{ id, deleted, object }`.
*/
export function buildUserDeletedOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
userId: { type: 'string', description: 'Deleted Clerk user ID (data.id)' },
deleted: { type: 'boolean', description: 'Whether the user was deleted (data.deleted)' },
}
}
/**
* Build outputs for `session.created` events.
* The `data` object is the Clerk Session object.
*/
export function buildSessionOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
sessionId: { type: 'string', description: 'Clerk session ID (data.id)' },
userId: { type: 'string', description: 'User the session belongs to (data.user_id)' },
clientId: { type: 'string', description: 'Client ID for the session (data.client_id)' },
status: { type: 'string', description: 'Session status (data.status)' },
createdAt: { type: 'number', description: 'Session creation timestamp (data.created_at)' },
}
}
/**
* Build outputs for `organization.created` events.
* The `data` object is the Clerk Organization object.
*/
export function buildOrganizationOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
organizationId: { type: 'string', description: 'Clerk organization ID (data.id)' },
name: { type: 'string', description: 'Organization name (data.name)' },
slug: { type: 'string', description: 'Organization slug (data.slug)' },
createdBy: { type: 'string', description: 'User ID of the creator (data.created_by)' },
membersCount: { type: 'number', description: 'Number of members (data.members_count)' },
maxAllowedMemberships: {
type: 'number',
description: 'Maximum allowed memberships (data.max_allowed_memberships)',
},
createdAt: { type: 'number', description: 'Organization creation timestamp (data.created_at)' },
}
}
/**
* Build outputs for `organizationMembership.created` and `.updated` events.
* The `data` object is the Clerk OrganizationMembership object.
*/
export function buildOrganizationMembershipOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
membershipId: { type: 'string', description: 'Membership ID (data.id)' },
role: { type: 'string', description: 'Membership role, e.g. org:admin (data.role)' },
organizationId: {
type: 'string',
description: 'Organization ID (data.organization.id)',
},
userId: {
type: 'string',
description: 'User ID of the member (data.public_user_data.user_id)',
},
createdAt: { type: 'number', description: 'Membership creation timestamp (data.created_at)' },
}
}
/**
* Build outputs for `organization.deleted` events.
* The `data` object is a deleted-object marker: `{ id, deleted, object }`.
*/
export function buildOrganizationDeletedOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
organizationId: { type: 'string', description: 'Deleted Clerk organization ID (data.id)' },
deleted: {
type: 'boolean',
description: 'Whether the organization was deleted (data.deleted)',
},
}
}
/**
* Build outputs for `organizationMembership.deleted` events.
* The `data` object is a deleted-object marker: `{ id, deleted, object }`.
*/
export function buildOrganizationMembershipDeletedOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
membershipId: { type: 'string', description: 'Deleted membership ID (data.id)' },
deleted: { type: 'boolean', description: 'Whether the membership was deleted (data.deleted)' },
}
}
/**
* Build outputs for the generic webhook (all events).
* Only the fields common to every Clerk event are guaranteed; use `data`
* for event-specific fields.
*/
export function buildClerkOutputs(): Record<string, TriggerOutput> {
return {
...commonEventOutputs,
}
}
/**
* Check if a Clerk event payload matches the expected trigger configuration.
*/
export function isClerkEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
const expectedType = CLERK_TRIGGER_TO_EVENT_TYPE[triggerId]
if (!expectedType) {
return true // Unknown trigger or generic webhook, allow through
}
return (body?.type as string | undefined) === expectedType
}
+38
View File
@@ -0,0 +1,38 @@
import { ClerkIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildClerkExtraFields,
buildClerkOutputs,
clerkSetupInstructions,
clerkTriggerOptions,
} from '@/triggers/clerk/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Clerk Generic Webhook Trigger (all events).
* Triggers for any Clerk webhook event you subscribe the endpoint to.
*/
export const clerkWebhookTrigger: TriggerConfig = {
id: 'clerk_webhook',
name: 'Clerk Webhook',
provider: 'clerk',
description: 'Trigger workflow on any Clerk webhook event',
version: '1.0.0',
icon: ClerkIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'clerk_webhook',
triggerOptions: clerkTriggerOptions,
setupInstructions: clerkSetupInstructions('events you want to receive'),
extraFields: buildClerkExtraFields('clerk_webhook'),
}),
outputs: buildClerkOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildAttachmentOutputs,
buildConfluenceAttachmentExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Attachment Created Trigger
*
* Triggers when a new attachment is uploaded to a page or blog post in Confluence.
*/
export const confluenceAttachmentCreatedTrigger: TriggerConfig = {
id: 'confluence_attachment_created',
name: 'Confluence Attachment Created',
provider: 'confluence',
description: 'Trigger workflow when an attachment is uploaded in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_attachment_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('attachment_created'),
extraFields: buildConfluenceAttachmentExtraFields('confluence_attachment_created'),
}),
outputs: buildAttachmentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildAttachmentOutputs,
buildConfluenceAttachmentExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Attachment Removed Trigger
*
* Triggers when an attachment is removed or trashed from a page or blog post in Confluence.
*/
export const confluenceAttachmentRemovedTrigger: TriggerConfig = {
id: 'confluence_attachment_removed',
name: 'Confluence Attachment Removed',
provider: 'confluence',
description: 'Trigger workflow when an attachment is removed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_attachment_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('attachment_removed'),
extraFields: buildConfluenceAttachmentExtraFields('confluence_attachment_removed'),
}),
outputs: buildAttachmentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildAttachmentOutputs,
buildConfluenceAttachmentExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Attachment Updated Trigger
*
* Triggers when an attachment is updated (new version uploaded) in Confluence.
*/
export const confluenceAttachmentUpdatedTrigger: TriggerConfig = {
id: 'confluence_attachment_updated',
name: 'Confluence Attachment Updated',
provider: 'confluence',
description: 'Trigger workflow when an attachment is updated in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_attachment_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('attachment_updated'),
extraFields: buildConfluenceAttachmentExtraFields('confluence_attachment_updated'),
}),
outputs: buildAttachmentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBlogOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Blog Post Created Trigger
*
* Triggers when a new blog post is created in Confluence.
*/
export const confluenceBlogCreatedTrigger: TriggerConfig = {
id: 'confluence_blog_created',
name: 'Confluence Blog Post Created',
provider: 'confluence',
description: 'Trigger workflow when a blog post is created in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_blog_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('blog_created'),
extraFields: buildConfluenceExtraFields('confluence_blog_created'),
}),
outputs: buildBlogOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBlogOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Blog Post Removed Trigger
*
* Triggers when a blog post is removed or trashed in Confluence.
*/
export const confluenceBlogRemovedTrigger: TriggerConfig = {
id: 'confluence_blog_removed',
name: 'Confluence Blog Post Removed',
provider: 'confluence',
description: 'Trigger workflow when a blog post is removed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_blog_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('blog_removed'),
extraFields: buildConfluenceExtraFields('confluence_blog_removed'),
}),
outputs: buildBlogOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBlogOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Blog Post Restored Trigger
*
* Triggers when a blog post is restored from trash in Confluence.
*/
export const confluenceBlogRestoredTrigger: TriggerConfig = {
id: 'confluence_blog_restored',
name: 'Confluence Blog Post Restored',
provider: 'confluence',
description: 'Trigger workflow when a blog post is restored from trash in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_blog_restored',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('blog_restored'),
extraFields: buildConfluenceExtraFields('confluence_blog_restored'),
}),
outputs: buildBlogOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildBlogOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Blog Post Updated Trigger
*
* Triggers when a blog post is updated in Confluence.
*/
export const confluenceBlogUpdatedTrigger: TriggerConfig = {
id: 'confluence_blog_updated',
name: 'Confluence Blog Post Updated',
provider: 'confluence',
description: 'Trigger workflow when a blog post is updated in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_blog_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('blog_updated'),
extraFields: buildConfluenceExtraFields('confluence_blog_updated'),
}),
outputs: buildBlogOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildCommentOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Comment Created Trigger
*
* Triggers when a new comment is created on a page or blog post in Confluence.
*/
export const confluenceCommentCreatedTrigger: TriggerConfig = {
id: 'confluence_comment_created',
name: 'Confluence Comment Created',
provider: 'confluence',
description: 'Trigger workflow when a comment is created in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_comment_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('comment_created'),
extraFields: buildConfluenceExtraFields('confluence_comment_created'),
}),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildCommentOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Comment Removed Trigger
*
* Triggers when a comment is removed from a page or blog post in Confluence.
*/
export const confluenceCommentRemovedTrigger: TriggerConfig = {
id: 'confluence_comment_removed',
name: 'Confluence Comment Removed',
provider: 'confluence',
description: 'Trigger workflow when a comment is removed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_comment_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('comment_removed'),
extraFields: buildConfluenceExtraFields('confluence_comment_removed'),
}),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildCommentOutputs,
buildConfluenceExtraFields,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Comment Updated Trigger
*
* Triggers when a comment on a page or blog post is updated/edited in Confluence.
*/
export const confluenceCommentUpdatedTrigger: TriggerConfig = {
id: 'confluence_comment_updated',
name: 'Confluence Comment Updated',
provider: 'confluence',
description: 'Trigger workflow when a comment is updated in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_comment_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('comment_updated'),
extraFields: buildConfluenceExtraFields('confluence_comment_updated'),
}),
outputs: buildCommentOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
+28
View File
@@ -0,0 +1,28 @@
/**
* Confluence Triggers
* Export all Confluence webhook triggers
*/
export { confluenceAttachmentCreatedTrigger } from './attachment_created'
export { confluenceAttachmentRemovedTrigger } from './attachment_removed'
export { confluenceAttachmentUpdatedTrigger } from './attachment_updated'
export { confluenceBlogCreatedTrigger } from './blog_created'
export { confluenceBlogRemovedTrigger } from './blog_removed'
export { confluenceBlogRestoredTrigger } from './blog_restored'
export { confluenceBlogUpdatedTrigger } from './blog_updated'
export { confluenceCommentCreatedTrigger } from './comment_created'
export { confluenceCommentRemovedTrigger } from './comment_removed'
export { confluenceCommentUpdatedTrigger } from './comment_updated'
export { confluenceLabelAddedTrigger } from './label_added'
export { confluenceLabelRemovedTrigger } from './label_removed'
export { confluencePageCreatedTrigger } from './page_created'
export { confluencePageMovedTrigger } from './page_moved'
export { confluencePagePermissionsUpdatedTrigger } from './page_permissions_updated'
export { confluencePageRemovedTrigger } from './page_removed'
export { confluencePageRestoredTrigger } from './page_restored'
export { confluencePageUpdatedTrigger } from './page_updated'
export { confluenceSpaceCreatedTrigger } from './space_created'
export { confluenceSpaceRemovedTrigger } from './space_removed'
export { confluenceSpaceUpdatedTrigger } from './space_updated'
export { confluenceUserCreatedTrigger } from './user_created'
export { confluenceWebhookTrigger } from './webhook'
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildLabelOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Label Added Trigger
*
* Triggers when a label is added to a page, blog post, or other content in Confluence.
*/
export const confluenceLabelAddedTrigger: TriggerConfig = {
id: 'confluence_label_added',
name: 'Confluence Label Added',
provider: 'confluence',
description: 'Trigger workflow when a label is added to content in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_label_added',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('label_added'),
extraFields: buildConfluenceExtraFields('confluence_label_added'),
}),
outputs: buildLabelOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildLabelOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Label Removed Trigger
*
* Triggers when a label is removed from a page, blog post, or other content in Confluence.
*/
export const confluenceLabelRemovedTrigger: TriggerConfig = {
id: 'confluence_label_removed',
name: 'Confluence Label Removed',
provider: 'confluence',
description: 'Trigger workflow when a label is removed from content in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_label_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('label_removed'),
extraFields: buildConfluenceExtraFields('confluence_label_removed'),
}),
outputs: buildLabelOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,43 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPageOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Created Trigger
*
* This is the PRIMARY trigger - it includes the dropdown for selecting trigger type.
* Triggers when a new page is created in Confluence.
*/
export const confluencePageCreatedTrigger: TriggerConfig = {
id: 'confluence_page_created',
name: 'Confluence Page Created',
provider: 'confluence',
description: 'Trigger workflow when a new page is created in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_created',
triggerOptions: confluenceTriggerOptions,
includeDropdown: true,
setupInstructions: confluenceSetupInstructions('page_created'),
extraFields: buildConfluenceExtraFields('confluence_page_created'),
}),
outputs: buildPageOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPageOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Moved Trigger
*
* Triggers when a page is moved to a different space or parent in Confluence.
*/
export const confluencePageMovedTrigger: TriggerConfig = {
id: 'confluence_page_moved',
name: 'Confluence Page Moved',
provider: 'confluence',
description: 'Trigger workflow when a page is moved in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_moved',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('page_moved'),
extraFields: buildConfluenceExtraFields('confluence_page_moved'),
}),
outputs: buildPageOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPagePermissionsOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Permissions Updated Trigger
*
* Triggers when page permissions are changed in Confluence.
*/
export const confluencePagePermissionsUpdatedTrigger: TriggerConfig = {
id: 'confluence_page_permissions_updated',
name: 'Confluence Page Permissions Updated',
provider: 'confluence',
description: 'Trigger workflow when page permissions are changed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_permissions_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('content_permissions_updated'),
extraFields: buildConfluenceExtraFields('confluence_page_permissions_updated'),
}),
outputs: buildPagePermissionsOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPageOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Removed Trigger
*
* Triggers when a page is removed or trashed in Confluence.
*/
export const confluencePageRemovedTrigger: TriggerConfig = {
id: 'confluence_page_removed',
name: 'Confluence Page Removed',
provider: 'confluence',
description: 'Trigger workflow when a page is removed or trashed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('page_removed'),
extraFields: buildConfluenceExtraFields('confluence_page_removed'),
}),
outputs: buildPageOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPageOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Restored Trigger
*
* Triggers when a page is restored from trash in Confluence.
*/
export const confluencePageRestoredTrigger: TriggerConfig = {
id: 'confluence_page_restored',
name: 'Confluence Page Restored',
provider: 'confluence',
description: 'Trigger workflow when a page is restored from trash in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_restored',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('page_restored'),
extraFields: buildConfluenceExtraFields('confluence_page_restored'),
}),
outputs: buildPageOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildPageOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Page Updated Trigger
*
* Triggers when an existing page is updated in Confluence.
*/
export const confluencePageUpdatedTrigger: TriggerConfig = {
id: 'confluence_page_updated',
name: 'Confluence Page Updated',
provider: 'confluence',
description: 'Trigger workflow when a page is updated in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_page_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('page_updated'),
extraFields: buildConfluenceExtraFields('confluence_page_updated'),
}),
outputs: buildPageOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildSpaceOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Space Created Trigger
*
* Triggers when a new space is created in Confluence.
*/
export const confluenceSpaceCreatedTrigger: TriggerConfig = {
id: 'confluence_space_created',
name: 'Confluence Space Created',
provider: 'confluence',
description: 'Trigger workflow when a new space is created in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_space_created',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('space_created'),
extraFields: buildConfluenceExtraFields('confluence_space_created'),
}),
outputs: buildSpaceOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildSpaceOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Space Removed Trigger
*
* Triggers when a space is deleted/removed in Confluence.
*/
export const confluenceSpaceRemovedTrigger: TriggerConfig = {
id: 'confluence_space_removed',
name: 'Confluence Space Removed',
provider: 'confluence',
description: 'Trigger workflow when a space is removed in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_space_removed',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('space_removed'),
extraFields: buildConfluenceExtraFields('confluence_space_removed'),
}),
outputs: buildSpaceOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}
@@ -0,0 +1,41 @@
import { ConfluenceIcon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
buildConfluenceExtraFields,
buildSpaceOutputs,
confluenceSetupInstructions,
confluenceTriggerOptions,
} from '@/triggers/confluence/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* Confluence Space Updated Trigger
*
* Triggers when a space is updated (settings, permissions, etc.) in Confluence.
*/
export const confluenceSpaceUpdatedTrigger: TriggerConfig = {
id: 'confluence_space_updated',
name: 'Confluence Space Updated',
provider: 'confluence',
description: 'Trigger workflow when a space is updated in Confluence',
version: '1.0.0',
icon: ConfluenceIcon,
subBlocks: buildTriggerSubBlocks({
triggerId: 'confluence_space_updated',
triggerOptions: confluenceTriggerOptions,
setupInstructions: confluenceSetupInstructions('space_updated'),
extraFields: buildConfluenceExtraFields('confluence_space_updated'),
}),
outputs: buildSpaceOutputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hub-Signature': 'sha256=...',
'X-Atlassian-Webhook-Identifier': 'unique-webhook-id',
},
},
}

Some files were not shown because too many files have changed in this diff Show More