mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-06 21:54:01 -05:00
fix(grain): updated grain trigger to auto-establish trigger (#2666)
Co-authored-by: aadamgough <adam@sim.ai>
This commit is contained in:
@@ -581,6 +581,56 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
// --- End RSS specific logic ---
|
||||
|
||||
if (savedWebhook && provider === 'grain') {
|
||||
logger.info(`[${requestId}] Grain provider detected. Creating Grain webhook subscription.`)
|
||||
try {
|
||||
const grainHookId = await createGrainWebhookSubscription(
|
||||
request,
|
||||
{
|
||||
id: savedWebhook.id,
|
||||
path: savedWebhook.path,
|
||||
providerConfig: savedWebhook.providerConfig,
|
||||
},
|
||||
requestId
|
||||
)
|
||||
|
||||
if (grainHookId) {
|
||||
// Update the webhook record with the external Grain hook ID
|
||||
const updatedConfig = {
|
||||
...(savedWebhook.providerConfig as Record<string, any>),
|
||||
externalId: grainHookId,
|
||||
}
|
||||
await db
|
||||
.update(webhook)
|
||||
.set({
|
||||
providerConfig: updatedConfig,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(webhook.id, savedWebhook.id))
|
||||
|
||||
savedWebhook.providerConfig = updatedConfig
|
||||
logger.info(`[${requestId}] Successfully created Grain webhook`, {
|
||||
grainHookId,
|
||||
webhookId: savedWebhook.id,
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`[${requestId}] Error creating Grain webhook subscription, rolling back webhook`,
|
||||
err
|
||||
)
|
||||
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id))
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to create webhook in Grain',
|
||||
details: err instanceof Error ? err.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
// --- End Grain specific logic ---
|
||||
|
||||
const status = targetWebhookId ? 200 : 201
|
||||
return NextResponse.json({ webhook: savedWebhook }, { status })
|
||||
} catch (error: any) {
|
||||
@@ -947,3 +997,103 @@ async function createWebflowWebhookSubscription(
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create the webhook subscription in Grain
|
||||
async function createGrainWebhookSubscription(
|
||||
request: NextRequest,
|
||||
webhookData: any,
|
||||
requestId: string
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
const { path, providerConfig } = webhookData
|
||||
const { apiKey, includeHighlights, includeParticipants, includeAiSummary } =
|
||||
providerConfig || {}
|
||||
|
||||
if (!apiKey) {
|
||||
logger.warn(`[${requestId}] Missing apiKey for Grain webhook creation.`, {
|
||||
webhookId: webhookData.id,
|
||||
})
|
||||
throw new Error(
|
||||
'Grain API Key is required. Please provide your Grain Personal Access Token in the trigger configuration.'
|
||||
)
|
||||
}
|
||||
|
||||
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`
|
||||
|
||||
const grainApiUrl = 'https://api.grain.com/_/public-api/v2/hooks/create'
|
||||
|
||||
const requestBody: Record<string, any> = {
|
||||
hook_url: notificationUrl,
|
||||
}
|
||||
|
||||
// Build include object based on configuration
|
||||
const include: Record<string, boolean> = {}
|
||||
if (includeHighlights) {
|
||||
include.highlights = true
|
||||
}
|
||||
if (includeParticipants) {
|
||||
include.participants = true
|
||||
}
|
||||
if (includeAiSummary) {
|
||||
include.ai_summary = true
|
||||
}
|
||||
if (Object.keys(include).length > 0) {
|
||||
requestBody.include = include
|
||||
}
|
||||
|
||||
const grainResponse = await fetch(grainApiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Public-Api-Version': '2025-10-31',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
|
||||
const responseBody = await grainResponse.json()
|
||||
|
||||
if (!grainResponse.ok || responseBody.error) {
|
||||
const errorMessage =
|
||||
responseBody.error?.message ||
|
||||
responseBody.error ||
|
||||
responseBody.message ||
|
||||
'Unknown Grain API error'
|
||||
logger.error(
|
||||
`[${requestId}] Failed to create webhook in Grain for webhook ${webhookData.id}. Status: ${grainResponse.status}`,
|
||||
{ message: errorMessage, response: responseBody }
|
||||
)
|
||||
|
||||
let userFriendlyMessage = 'Failed to create webhook subscription in Grain'
|
||||
if (grainResponse.status === 401) {
|
||||
userFriendlyMessage =
|
||||
'Invalid Grain API Key. Please verify your Personal Access Token is correct.'
|
||||
} else if (grainResponse.status === 403) {
|
||||
userFriendlyMessage =
|
||||
'Access denied. Please ensure your Grain API Key has appropriate permissions.'
|
||||
} else if (errorMessage && errorMessage !== 'Unknown Grain API error') {
|
||||
userFriendlyMessage = `Grain error: ${errorMessage}`
|
||||
}
|
||||
|
||||
throw new Error(userFriendlyMessage)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Successfully created webhook in Grain for webhook ${webhookData.id}.`,
|
||||
{
|
||||
grainWebhookId: responseBody.id,
|
||||
}
|
||||
)
|
||||
|
||||
return responseBody.id
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`[${requestId}] Exception during Grain webhook creation for webhook ${webhookData.id}.`,
|
||||
{
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const telegramLogger = createLogger('TelegramWebhook')
|
||||
const airtableLogger = createLogger('AirtableWebhook')
|
||||
const typeformLogger = createLogger('TypeformWebhook')
|
||||
const calendlyLogger = createLogger('CalendlyWebhook')
|
||||
const grainLogger = createLogger('GrainWebhook')
|
||||
|
||||
function getProviderConfig(webhook: any): Record<string, any> {
|
||||
return (webhook.providerConfig as Record<string, any>) || {}
|
||||
@@ -661,9 +662,58 @@ export async function deleteCalendlyWebhook(webhook: any, requestId: string): Pr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Grain webhook
|
||||
* Don't fail webhook deletion if cleanup fails
|
||||
*/
|
||||
export async function deleteGrainWebhook(webhook: any, requestId: string): Promise<void> {
|
||||
try {
|
||||
const config = getProviderConfig(webhook)
|
||||
const apiKey = config.apiKey as string | undefined
|
||||
const externalId = config.externalId as string | undefined
|
||||
|
||||
if (!apiKey) {
|
||||
grainLogger.warn(
|
||||
`[${requestId}] Missing apiKey for Grain webhook deletion ${webhook.id}, skipping cleanup`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (!externalId) {
|
||||
grainLogger.warn(
|
||||
`[${requestId}] Missing externalId for Grain webhook deletion ${webhook.id}, skipping cleanup`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const grainApiUrl = `https://api.grain.com/_/public-api/v2/hooks/${externalId}`
|
||||
|
||||
const grainResponse = await fetch(grainApiUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Public-Api-Version': '2025-10-31',
|
||||
},
|
||||
})
|
||||
|
||||
if (!grainResponse.ok && grainResponse.status !== 404) {
|
||||
const responseBody = await grainResponse.json().catch(() => ({}))
|
||||
grainLogger.warn(
|
||||
`[${requestId}] Failed to delete Grain webhook (non-fatal): ${grainResponse.status}`,
|
||||
{ response: responseBody }
|
||||
)
|
||||
} else {
|
||||
grainLogger.info(`[${requestId}] Successfully deleted Grain webhook ${externalId}`)
|
||||
}
|
||||
} catch (error) {
|
||||
grainLogger.warn(`[${requestId}] Error deleting Grain webhook (non-fatal)`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up external webhook subscriptions for a webhook
|
||||
* Handles Airtable, Teams, Telegram, Typeform, and Calendly cleanup
|
||||
* Handles Airtable, Teams, Telegram, Typeform, Calendly, and Grain cleanup
|
||||
* Don't fail deletion if cleanup fails
|
||||
*/
|
||||
export async function cleanupExternalWebhook(
|
||||
@@ -681,5 +731,7 @@ export async function cleanupExternalWebhook(
|
||||
await deleteTypeformWebhook(webhook, requestId)
|
||||
} else if (webhook.provider === 'calendly') {
|
||||
await deleteCalendlyWebhook(webhook, requestId)
|
||||
} else if (webhook.provider === 'grain') {
|
||||
await deleteGrainWebhook(webhook, requestId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ export const grainHighlightCreatedTrigger: TriggerConfig = {
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -26,13 +26,35 @@ export const grainHighlightCreatedTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_highlight_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_highlight_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
@@ -12,13 +12,13 @@ export const grainHighlightUpdatedTrigger: TriggerConfig = {
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -26,13 +26,35 @@ export const grainHighlightUpdatedTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_highlight_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_highlight_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
@@ -12,13 +12,13 @@ export const grainRecordingCreatedTrigger: TriggerConfig = {
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -26,13 +26,35 @@ export const grainRecordingCreatedTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_recording_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_recording_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
@@ -12,13 +12,13 @@ export const grainRecordingUpdatedTrigger: TriggerConfig = {
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -26,13 +26,35 @@ export const grainRecordingUpdatedTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_recording_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_recording_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
@@ -12,13 +12,13 @@ export const grainStoryCreatedTrigger: TriggerConfig = {
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -26,13 +26,35 @@ export const grainStoryCreatedTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_story_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_story_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
@@ -17,19 +17,17 @@ export const grainTriggerOptions = [
|
||||
*/
|
||||
export function grainSetupInstructions(eventType: string): string {
|
||||
const instructions = [
|
||||
'<strong>Note:</strong> You need admin permissions in your Grain workspace to create webhooks.',
|
||||
'In Grain, navigate to <strong>Settings > Integrations > Webhooks</strong>.',
|
||||
'Click <strong>"Create webhook"</strong> or <strong>"Add webhook"</strong>.',
|
||||
'Paste the <strong>Webhook URL</strong> from above into the URL field.',
|
||||
'Optionally, enter the <strong>Webhook Secret</strong> from above for signature validation.',
|
||||
`Select the event types this webhook should listen to. For this trigger, select <strong>${eventType}</strong>.`,
|
||||
'Click <strong>"Save"</strong> to activate the webhook.',
|
||||
'Enter your Grain API Key (Personal Access Token) above.',
|
||||
'You can find or create your API key in Grain at <strong>Settings > Integrations > API</strong>.',
|
||||
'Optionally configure filters to narrow which recordings trigger the webhook.',
|
||||
`Click <strong>"Save Configuration"</strong> to automatically create the webhook in Grain for <strong>${eventType}</strong> events.`,
|
||||
'The webhook will be automatically deleted when you remove this trigger.',
|
||||
]
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ export const grainWebhookTrigger: TriggerConfig = {
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: 'Webhook URL will be generated',
|
||||
placeholder: 'Enter your Grain API key (Personal Access Token)',
|
||||
description: 'Required to create the webhook in Grain.',
|
||||
password: true,
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
@@ -35,13 +35,35 @@ export const grainWebhookTrigger: TriggerConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'webhookSecret',
|
||||
title: 'Webhook Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter a strong secret',
|
||||
description: 'Validates that webhook deliveries originate from Grain.',
|
||||
password: true,
|
||||
required: false,
|
||||
id: 'includeHighlights',
|
||||
title: 'Include Highlights',
|
||||
type: 'switch',
|
||||
description: 'Include highlights/clips in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeParticipants',
|
||||
title: 'Include Participants',
|
||||
type: 'switch',
|
||||
description: 'Include participant list in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'grain_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeAiSummary',
|
||||
title: 'Include AI Summary',
|
||||
type: 'switch',
|
||||
description: 'Include AI-generated summary in webhook payload.',
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
|
||||
Reference in New Issue
Block a user