mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
working checkbox, need to add zapier oauth2
This commit is contained in:
@@ -19,10 +19,15 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Execute Action', id: 'execute' },
|
||||
{ label: 'Stateless Execute', id: 'stateless_execute' },
|
||||
{ label: 'List Actions', id: 'list' },
|
||||
{ label: 'Search Apps', id: 'search_apps' },
|
||||
{ label: 'Search App Actions', id: 'search_app_actions' },
|
||||
{ label: 'Find Actions', id: 'guess' },
|
||||
{ label: 'Get Action Details', id: 'get_action_details' },
|
||||
{ label: 'Create Action', id: 'create' },
|
||||
{ label: 'Update Action', id: 'update' },
|
||||
{ label: 'Delete Action', id: 'delete' },
|
||||
],
|
||||
value: () => 'execute',
|
||||
},
|
||||
@@ -111,9 +116,9 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
title: 'Action Types',
|
||||
type: 'checkbox-list',
|
||||
options: [
|
||||
{ label: 'Write (Create/Send)', id: 'write' },
|
||||
{ label: 'Search (Find)', id: 'search' },
|
||||
{ label: 'Read (Get)', id: 'read' },
|
||||
{ label: 'Write (Create/Send)', id: 'actionTypes_write' },
|
||||
{ label: 'Search (Find)', id: 'actionTypes_search' },
|
||||
{ label: 'Read (Get)', id: 'actionTypes_read' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
@@ -178,6 +183,266 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
value: 'create',
|
||||
},
|
||||
},
|
||||
// Stateless Execute fields
|
||||
{
|
||||
id: 'statelessApp',
|
||||
title: 'App',
|
||||
type: 'short-input',
|
||||
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'statelessAction',
|
||||
title: 'Action',
|
||||
type: 'short-input',
|
||||
placeholder: 'Action identifier (e.g., "send_channel_message")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'statelessInstructions',
|
||||
title: 'Instructions',
|
||||
type: 'long-input',
|
||||
placeholder: 'Describe what you want to do in plain English',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'statelessActionType',
|
||||
title: 'Action Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Write', id: 'write' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Read', id: 'read' },
|
||||
],
|
||||
value: () => 'write',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'statelessParams',
|
||||
title: 'Parameters',
|
||||
type: 'code',
|
||||
placeholder: '{\n "channel": {"mode": "locked", "value": "#general"}\n}',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'statelessPreviewOnly',
|
||||
title: 'Preview Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Execute', id: 'false' },
|
||||
{ label: 'Preview Only', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'stateless_execute',
|
||||
},
|
||||
},
|
||||
// Search App Actions fields
|
||||
{
|
||||
id: 'searchAppActionsApp',
|
||||
title: 'App',
|
||||
type: 'short-input',
|
||||
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'search_app_actions',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'searchAppActionsQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional: filter actions by name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'search_app_actions',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'searchAppActionsTypes',
|
||||
title: 'Action Types',
|
||||
type: 'checkbox-list',
|
||||
options: [
|
||||
{ label: 'Write (Create/Send)', id: 'searchAppActionsTypes_write' },
|
||||
{ label: 'Search (Find)', id: 'searchAppActionsTypes_search' },
|
||||
{ label: 'Read (Get)', id: 'searchAppActionsTypes_read' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'search_app_actions',
|
||||
},
|
||||
},
|
||||
// Get Action Details fields
|
||||
{
|
||||
id: 'detailsApp',
|
||||
title: 'App',
|
||||
type: 'short-input',
|
||||
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'detailsAction',
|
||||
title: 'Action',
|
||||
type: 'short-input',
|
||||
placeholder: 'Action identifier (e.g., "send_channel_message")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'detailsActionType',
|
||||
title: 'Action Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Write', id: 'write' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Read', id: 'read' },
|
||||
],
|
||||
value: () => 'write',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeNeeds',
|
||||
title: 'Include Inputs (Needs)',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeGives',
|
||||
title: 'Include Outputs (Gives)',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'includeSample',
|
||||
title: 'Include Sample',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'get_action_details',
|
||||
},
|
||||
},
|
||||
// Update Action fields
|
||||
{
|
||||
id: 'updateActionId',
|
||||
title: 'Action ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'The ID of the AI Action to update',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'updateApp',
|
||||
title: 'App',
|
||||
type: 'short-input',
|
||||
placeholder: 'App identifier (e.g., "SlackAPI")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'updateAction',
|
||||
title: 'Action',
|
||||
type: 'short-input',
|
||||
placeholder: 'Action identifier (e.g., "send_channel_message")',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'updateActionType',
|
||||
title: 'Action Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Write', id: 'write' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Read', id: 'read' },
|
||||
],
|
||||
value: () => 'write',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'updateParams',
|
||||
title: 'Parameters',
|
||||
type: 'code',
|
||||
placeholder: '{\n "channel": "#general"\n}',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
},
|
||||
},
|
||||
// Delete Action fields
|
||||
{
|
||||
id: 'deleteActionId',
|
||||
title: 'Action ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'The ID of the AI Action to delete',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'delete',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
@@ -197,14 +462,24 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
switch (params.operation) {
|
||||
case 'execute':
|
||||
return 'zapier_execute_action'
|
||||
case 'stateless_execute':
|
||||
return 'zapier_stateless_execute'
|
||||
case 'list':
|
||||
return 'zapier_list_actions'
|
||||
case 'search_apps':
|
||||
return 'zapier_search_apps'
|
||||
case 'search_app_actions':
|
||||
return 'zapier_search_app_actions'
|
||||
case 'guess':
|
||||
return 'zapier_guess_actions'
|
||||
case 'get_action_details':
|
||||
return 'zapier_get_action_details'
|
||||
case 'create':
|
||||
return 'zapier_create_action'
|
||||
case 'update':
|
||||
return 'zapier_update_action'
|
||||
case 'delete':
|
||||
return 'zapier_delete_action'
|
||||
default:
|
||||
throw new Error(`Invalid Zapier operation: ${params.operation}`)
|
||||
}
|
||||
@@ -224,88 +499,121 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
action,
|
||||
createActionType,
|
||||
createParams,
|
||||
statelessApp,
|
||||
statelessAction,
|
||||
statelessInstructions,
|
||||
statelessActionType,
|
||||
statelessParams,
|
||||
statelessPreviewOnly,
|
||||
searchAppActionsApp,
|
||||
searchAppActionsQuery,
|
||||
detailsApp,
|
||||
detailsAction,
|
||||
detailsActionType,
|
||||
includeNeeds,
|
||||
includeGives,
|
||||
includeSample,
|
||||
updateActionId,
|
||||
updateApp,
|
||||
updateAction,
|
||||
updateActionType,
|
||||
updateParams,
|
||||
deleteActionId,
|
||||
} = params
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('Zapier API key is required')
|
||||
const baseParams: Record<string, any> = { apiKey }
|
||||
|
||||
// Helper to parse JSON params
|
||||
const parseJsonParams = (jsonParams: any) => {
|
||||
if (!jsonParams) return undefined
|
||||
try {
|
||||
return typeof jsonParams === 'string' ? JSON.parse(jsonParams) : jsonParams
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in parameters field')
|
||||
}
|
||||
}
|
||||
|
||||
const baseParams: Record<string, any> = {
|
||||
apiKey,
|
||||
// Helper to collect checkbox-list values
|
||||
// Use truthy check since values may be boolean true or string "true" after serialization
|
||||
const collectActionTypes = (prefix: string) => {
|
||||
const types: string[] = []
|
||||
const writeVal = params[`${prefix}_write`]
|
||||
const searchVal = params[`${prefix}_search`]
|
||||
const readVal = params[`${prefix}_read`]
|
||||
if (writeVal === true || writeVal === 'true') types.push('write')
|
||||
if (searchVal === true || searchVal === 'true') types.push('search')
|
||||
if (readVal === true || readVal === 'true') types.push('read')
|
||||
return types.length > 0 ? types : undefined
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case 'execute': {
|
||||
if (!actionId) {
|
||||
throw new Error('Action ID is required for execute operation')
|
||||
}
|
||||
if (!instructions) {
|
||||
throw new Error('Instructions are required for execute operation')
|
||||
}
|
||||
case 'execute':
|
||||
baseParams.actionId = actionId
|
||||
baseParams.instructions = instructions
|
||||
if (execParams) {
|
||||
try {
|
||||
baseParams.params =
|
||||
typeof execParams === 'string' ? JSON.parse(execParams) : execParams
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in parameters field')
|
||||
}
|
||||
}
|
||||
baseParams.params = parseJsonParams(execParams)
|
||||
baseParams.previewOnly = previewOnly === 'true'
|
||||
break
|
||||
}
|
||||
|
||||
case 'stateless_execute':
|
||||
baseParams.app = statelessApp
|
||||
baseParams.action = statelessAction
|
||||
baseParams.instructions = statelessInstructions
|
||||
baseParams.actionType = statelessActionType || 'write'
|
||||
baseParams.params = parseJsonParams(statelessParams)
|
||||
baseParams.previewOnly = statelessPreviewOnly === 'true'
|
||||
break
|
||||
|
||||
case 'list':
|
||||
break
|
||||
|
||||
case 'search_apps':
|
||||
if (searchQuery) {
|
||||
baseParams.query = searchQuery
|
||||
}
|
||||
if (searchQuery) baseParams.query = searchQuery
|
||||
break
|
||||
|
||||
case 'search_app_actions':
|
||||
baseParams.app = searchAppActionsApp
|
||||
if (searchAppActionsQuery) baseParams.query = searchAppActionsQuery
|
||||
baseParams.actionTypes = collectActionTypes('searchAppActionsTypes')
|
||||
break
|
||||
|
||||
case 'guess': {
|
||||
if (!guessQuery) {
|
||||
throw new Error('Search query is required for find actions operation')
|
||||
}
|
||||
baseParams.query = guessQuery
|
||||
const actionTypes: string[] = []
|
||||
if (params.write === true) actionTypes.push('write')
|
||||
if (params.search === true) actionTypes.push('search')
|
||||
if (params.read === true) actionTypes.push('read')
|
||||
if (actionTypes.length > 0) {
|
||||
baseParams.actionTypes = actionTypes
|
||||
}
|
||||
// Checkbox-list values are stored under prefixed option IDs (actionTypes_write, etc.)
|
||||
baseParams.actionTypes = collectActionTypes('actionTypes')
|
||||
if (resultCount) {
|
||||
const count = Number.parseInt(resultCount, 10)
|
||||
if (!Number.isNaN(count)) {
|
||||
baseParams.count = count
|
||||
}
|
||||
if (!Number.isNaN(count)) baseParams.count = count
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'create': {
|
||||
if (!app) {
|
||||
throw new Error('App is required for create action operation')
|
||||
}
|
||||
if (!action) {
|
||||
throw new Error('Action is required for create action operation')
|
||||
}
|
||||
case 'get_action_details':
|
||||
baseParams.app = detailsApp
|
||||
baseParams.action = detailsAction
|
||||
baseParams.actionType = detailsActionType || 'write'
|
||||
baseParams.includeNeeds = includeNeeds !== 'false'
|
||||
baseParams.includeGives = includeGives === 'true'
|
||||
baseParams.includeSample = includeSample === 'true'
|
||||
break
|
||||
|
||||
case 'create':
|
||||
baseParams.app = app
|
||||
baseParams.action = action
|
||||
baseParams.actionType = createActionType || 'write'
|
||||
if (createParams) {
|
||||
try {
|
||||
baseParams.params =
|
||||
typeof createParams === 'string' ? JSON.parse(createParams) : createParams
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in parameters field')
|
||||
}
|
||||
}
|
||||
baseParams.params = parseJsonParams(createParams)
|
||||
break
|
||||
|
||||
case 'update':
|
||||
baseParams.actionId = updateActionId
|
||||
baseParams.app = updateApp
|
||||
baseParams.action = updateAction
|
||||
baseParams.actionType = updateActionType || 'write'
|
||||
baseParams.params = parseJsonParams(updateParams)
|
||||
break
|
||||
|
||||
case 'delete':
|
||||
baseParams.actionId = deleteActionId
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return baseParams
|
||||
@@ -320,19 +628,47 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
instructions: { type: 'string', description: 'Plain English instructions for the action' },
|
||||
params: { type: 'json', description: 'Optional parameter constraints' },
|
||||
previewOnly: { type: 'string', description: 'Whether to preview without executing' },
|
||||
// Stateless execute inputs
|
||||
statelessApp: { type: 'string', description: 'App identifier for stateless execute' },
|
||||
statelessAction: { type: 'string', description: 'Action identifier for stateless execute' },
|
||||
statelessInstructions: { type: 'string', description: 'Instructions for stateless execute' },
|
||||
statelessActionType: { type: 'string', description: 'Action type for stateless execute' },
|
||||
statelessParams: { type: 'json', description: 'Parameters for stateless execute' },
|
||||
statelessPreviewOnly: { type: 'string', description: 'Preview mode for stateless execute' },
|
||||
// Search inputs
|
||||
searchQuery: { type: 'string', description: 'App search query' },
|
||||
// Search app actions inputs
|
||||
searchAppActionsApp: { type: 'string', description: 'App to search actions for' },
|
||||
searchAppActionsQuery: { type: 'string', description: 'Query to filter actions' },
|
||||
searchAppActionsTypes_write: { type: 'boolean', description: 'Include write actions' },
|
||||
searchAppActionsTypes_search: { type: 'boolean', description: 'Include search actions' },
|
||||
searchAppActionsTypes_read: { type: 'boolean', description: 'Include read actions' },
|
||||
// Guess inputs
|
||||
guessQuery: { type: 'string', description: 'Natural language query to find actions' },
|
||||
write: { type: 'boolean', description: 'Include write actions' },
|
||||
search: { type: 'boolean', description: 'Include search actions' },
|
||||
read: { type: 'boolean', description: 'Include read actions' },
|
||||
actionTypes_write: { type: 'boolean', description: 'Include write actions' },
|
||||
actionTypes_search: { type: 'boolean', description: 'Include search actions' },
|
||||
actionTypes_read: { type: 'boolean', description: 'Include read actions' },
|
||||
resultCount: { type: 'string', description: 'Maximum number of results' },
|
||||
// Get action details inputs
|
||||
detailsApp: { type: 'string', description: 'App identifier for action details' },
|
||||
detailsAction: { type: 'string', description: 'Action identifier for action details' },
|
||||
detailsActionType: { type: 'string', description: 'Action type for action details' },
|
||||
includeNeeds: { type: 'string', description: 'Include input requirements' },
|
||||
includeGives: { type: 'string', description: 'Include output specifications' },
|
||||
includeSample: { type: 'string', description: 'Include sample data' },
|
||||
// Create inputs
|
||||
app: { type: 'string', description: 'App identifier' },
|
||||
action: { type: 'string', description: 'Action identifier' },
|
||||
createActionType: { type: 'string', description: 'Type of action to create' },
|
||||
createParams: { type: 'json', description: 'Pre-configured parameters' },
|
||||
// Update inputs
|
||||
updateActionId: { type: 'string', description: 'AI Action ID to update' },
|
||||
updateApp: { type: 'string', description: 'App identifier for update' },
|
||||
updateAction: { type: 'string', description: 'Action identifier for update' },
|
||||
updateActionType: { type: 'string', description: 'Action type for update' },
|
||||
updateParams: { type: 'json', description: 'Parameters for update' },
|
||||
// Delete inputs
|
||||
deleteActionId: { type: 'string', description: 'AI Action ID to delete' },
|
||||
},
|
||||
outputs: {
|
||||
// Execute Action outputs
|
||||
@@ -439,5 +775,31 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
|
||||
type: 'number',
|
||||
description: 'Authentication ID used for the app',
|
||||
},
|
||||
// Get Action Details outputs
|
||||
needs: {
|
||||
type: 'json',
|
||||
description: 'Array of input requirements for the action',
|
||||
},
|
||||
gives: {
|
||||
type: 'json',
|
||||
description: 'Array of output fields from the action',
|
||||
},
|
||||
sample: {
|
||||
type: 'json',
|
||||
description: 'Sample execution result',
|
||||
},
|
||||
customNeedsProbability: {
|
||||
type: 'number',
|
||||
description: 'Probability that action has custom/dynamic input fields',
|
||||
},
|
||||
// Delete Action outputs
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the action was successfully deleted',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Status message for delete operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -115,10 +115,6 @@ export class Serializer {
|
||||
safeParallels
|
||||
)
|
||||
|
||||
if (validateRequired) {
|
||||
this.validateSubflowsBeforeExecution(blocks, safeLoops, safeParallels)
|
||||
}
|
||||
|
||||
return {
|
||||
version: '1.0',
|
||||
blocks: Object.values(blocks).map((block) =>
|
||||
@@ -139,18 +135,6 @@ export class Serializer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate loop and parallel subflows for required inputs when running in "each/collection" modes
|
||||
*/
|
||||
private validateSubflowsBeforeExecution(
|
||||
blocks: Record<string, BlockState>,
|
||||
loops: Record<string, Loop>,
|
||||
parallels: Record<string, Parallel>
|
||||
): void {
|
||||
// Note: Empty collections in forEach loops and parallel collection mode are handled gracefully
|
||||
// at runtime - the loop/parallel will simply be skipped. No build-time validation needed.
|
||||
}
|
||||
|
||||
private serializeBlock(
|
||||
block: BlockState,
|
||||
options: {
|
||||
@@ -367,6 +351,15 @@ export class Serializer {
|
||||
) {
|
||||
params[id] = subBlock.value
|
||||
}
|
||||
|
||||
if (subBlockConfig?.type === 'checkbox-list' && Array.isArray(subBlockConfig.options)) {
|
||||
subBlockConfig.options.forEach((option: { id: string; label: string }) => {
|
||||
const optionSubBlock = block.subBlocks[option.id]
|
||||
if (optionSubBlock !== undefined) {
|
||||
params[option.id] = optionSubBlock.value
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Then check for any subBlocks with default values
|
||||
|
||||
@@ -49,7 +49,7 @@ export const zapierExecuteActionTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://actions.zapier.com/api/v2/ai-actions/${encodeURIComponent(params.actionId)}/execute${params.previewOnly ? '?preview_only=true' : ''}`,
|
||||
`https://actions.zapier.com/api/v2/ai-actions/${encodeURIComponent(params.actionId)}/execute/${params.previewOnly ? '?preview_only=true' : ''}`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
Reference in New Issue
Block a user