@ -32,9 +32,12 @@ import { VariableForTestInputs } from './VariableForTestInputs'
|
||||
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
|
||||
import {
|
||||
HttpMethod,
|
||||
defaultTimeout,
|
||||
defaultWebhookAttributes,
|
||||
defaultWebhookBlockOptions,
|
||||
maxTimeout,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
import { NumberInput } from '@/components/inputs'
|
||||
|
||||
type Props = {
|
||||
blockId: string
|
||||
@ -81,6 +84,9 @@ export const WebhookAdvancedConfigForm = ({
|
||||
const updateIsCustomBody = (isCustomBody: boolean) =>
|
||||
onOptionsChange({ ...options, isCustomBody })
|
||||
|
||||
const updateTimeout = (timeout: number | undefined) =>
|
||||
onOptionsChange({ ...options, timeout })
|
||||
|
||||
const executeTestRequest = async () => {
|
||||
if (!typebot) return
|
||||
setIsTestResponseLoading(true)
|
||||
@ -196,6 +202,22 @@ export const WebhookAdvancedConfigForm = ({
|
||||
)}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<AccordionButton justifyContent="space-between">
|
||||
Advanced parameters
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pt="4">
|
||||
<NumberInput
|
||||
label="Timeout (s)"
|
||||
defaultValue={options?.timeout ?? defaultTimeout}
|
||||
min={1}
|
||||
max={maxTimeout}
|
||||
onValueChange={updateTimeout}
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<AccordionButton justifyContent="space-between">
|
||||
Variable values for test
|
||||
|
9
apps/builder/src/pages/api/test.ts
Normal file
9
apps/builder/src/pages/api/test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 11000))
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
@ -108,6 +108,10 @@ Possibilities are endless when it comes to API calls, you can litteraly call any
|
||||
|
||||
Feel free to ask the [community](https://typebot.io/discord) for help if you struggle setting up a Webhook block.
|
||||
|
||||
## Timeout
|
||||
|
||||
By default, the Webhook block will wait 10 seconds for the 3rd party service to respond. If it doesn't respond in time, the block will fail. You can customize this timeout value in the "Advanced params" section of your Webhook block settings.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
The Webhook block request fail or didn't seem to trigger? Make sure to check the [logs](/results/overview#logs). If you still can't figure out what went wrong, shoot me a message using the chat button directly in the tool 👍
|
||||
|
@ -1411,6 +1411,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1863,6 +1868,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2077,6 +2087,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2226,6 +2241,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -7595,6 +7615,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8047,6 +8072,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8261,6 +8291,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8410,6 +8445,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12096,6 +12136,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12548,6 +12593,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12762,6 +12812,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12911,6 +12966,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -25701,6 +25761,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26144,6 +26209,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26349,6 +26419,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26489,6 +26564,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28977,6 +29057,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -29429,6 +29514,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -29643,6 +29733,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -29792,6 +29887,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -31766,6 +31866,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -32218,6 +32323,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -32432,6 +32542,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -32581,6 +32696,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5529,6 +5529,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5981,6 +5986,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -6195,6 +6205,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -6344,6 +6359,11 @@
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -9211,6 +9231,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9654,6 +9679,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9859,6 +9889,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9999,6 +10034,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,14 +23,14 @@ import { saveSuccessLog } from '@typebot.io/bot-engine/logs/saveSuccessLog'
|
||||
import { parseSampleResult } from '@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult'
|
||||
import {
|
||||
HttpMethod,
|
||||
defaultTimeout,
|
||||
defaultWebhookAttributes,
|
||||
maxTimeout,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
import {
|
||||
convertKeyValueTableToObject,
|
||||
longReqTimeoutWhitelist,
|
||||
longRequestTimeout,
|
||||
responseDefaultTimeout,
|
||||
} from '@typebot.io/bot-engine/blocks/integrations/webhook/executeWebhookBlock'
|
||||
|
||||
const cors = initMiddleware(Cors())
|
||||
@ -184,7 +184,7 @@ export const executeWebhook =
|
||||
: undefined,
|
||||
body: body && !isJson ? body : undefined,
|
||||
timeout: {
|
||||
response: isLongRequest ? longRequestTimeout : responseDefaultTimeout,
|
||||
response: isLongRequest ? maxTimeout : defaultTimeout,
|
||||
},
|
||||
}
|
||||
try {
|
||||
|
@ -22,7 +22,9 @@ import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import {
|
||||
HttpMethod,
|
||||
defaultTimeout,
|
||||
defaultWebhookAttributes,
|
||||
maxTimeout,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
|
||||
type ParsedWebhook = ExecutableWebhook & {
|
||||
@ -30,9 +32,6 @@ type ParsedWebhook = ExecutableWebhook & {
|
||||
isJson: boolean
|
||||
}
|
||||
|
||||
export const responseDefaultTimeout = 10000
|
||||
export const longRequestTimeout = 120000
|
||||
|
||||
export const longReqTimeoutWhitelist = [
|
||||
'https://api.openai.com',
|
||||
'https://retune.so',
|
||||
@ -44,7 +43,7 @@ export const longReqTimeoutWhitelist = [
|
||||
export const webhookSuccessDescription = `Webhook successfuly executed.`
|
||||
export const webhookErrorDescription = `Webhook returned an error.`
|
||||
|
||||
type Params = { disableRequestTimeout?: boolean }
|
||||
type Params = { disableRequestTimeout?: boolean; timeout?: number }
|
||||
|
||||
export const executeWebhookBlock = async (
|
||||
state: SessionState,
|
||||
@ -86,7 +85,10 @@ export const executeWebhookBlock = async (
|
||||
response: webhookResponse,
|
||||
logs: executeWebhookLogs,
|
||||
startTimeShouldBeUpdated,
|
||||
} = await executeWebhook(parsedWebhook, params)
|
||||
} = await executeWebhook(parsedWebhook, {
|
||||
...params,
|
||||
timeout: block.options?.timeout,
|
||||
})
|
||||
|
||||
return {
|
||||
...resumeWebhookExecution({
|
||||
@ -196,7 +198,12 @@ export const executeWebhook = async (
|
||||
contentType?.includes('x-www-form-urlencoded') && body ? body : undefined,
|
||||
body: body && !isJson ? (body as string) : undefined,
|
||||
timeout: {
|
||||
response: isLongRequest ? longRequestTimeout : responseDefaultTimeout,
|
||||
response:
|
||||
params.timeout && params.timeout !== defaultTimeout
|
||||
? Math.min(params.timeout, maxTimeout) * 1000
|
||||
: isLongRequest
|
||||
? maxTimeout * 1000
|
||||
: defaultTimeout * 1000,
|
||||
},
|
||||
} satisfies OptionsInit
|
||||
|
||||
@ -207,8 +214,8 @@ export const executeWebhook = async (
|
||||
description: webhookSuccessDescription,
|
||||
details: {
|
||||
statusCode: response.statusCode,
|
||||
request,
|
||||
response: safeJsonParse(response.body).data,
|
||||
request,
|
||||
},
|
||||
})
|
||||
return {
|
||||
@ -217,7 +224,7 @@ export const executeWebhook = async (
|
||||
data: safeJsonParse(response.body).data,
|
||||
},
|
||||
logs,
|
||||
startTimeShouldBeUpdated: isLongRequest,
|
||||
startTimeShouldBeUpdated: true,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
@ -234,7 +241,27 @@ export const executeWebhook = async (
|
||||
response,
|
||||
},
|
||||
})
|
||||
return { response, logs, startTimeShouldBeUpdated: isLongRequest }
|
||||
return { response, logs, startTimeShouldBeUpdated: true }
|
||||
}
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
error &&
|
||||
'code' in error &&
|
||||
error.code === 'ETIMEDOUT'
|
||||
) {
|
||||
const response = {
|
||||
statusCode: 408,
|
||||
data: { message: `Request timed out.` },
|
||||
}
|
||||
logs.push({
|
||||
status: 'error',
|
||||
description: `Webhook request timed out. (${request.timeout.response}ms)`,
|
||||
details: {
|
||||
response,
|
||||
request,
|
||||
},
|
||||
})
|
||||
return { response, logs, startTimeShouldBeUpdated: true }
|
||||
}
|
||||
const response = {
|
||||
statusCode: 500,
|
||||
@ -245,11 +272,11 @@ export const executeWebhook = async (
|
||||
status: 'error',
|
||||
description: `Webhook failed to execute.`,
|
||||
details: {
|
||||
request,
|
||||
response,
|
||||
request,
|
||||
},
|
||||
})
|
||||
return { response, logs, startTimeShouldBeUpdated: isLongRequest }
|
||||
return { response, logs, startTimeShouldBeUpdated: true }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,3 +21,6 @@ export const defaultWebhookBlockOptions = {
|
||||
isCustomBody: false,
|
||||
isExecutedOnClient: false,
|
||||
} as const satisfies WebhookBlockV6['options']
|
||||
|
||||
export const defaultTimeout = 10
|
||||
export const maxTimeout = 120
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { z } from '../../../../zod'
|
||||
import { blockBaseSchema } from '../../shared'
|
||||
import { IntegrationBlockType } from '../constants'
|
||||
import { HttpMethod } from './constants'
|
||||
import { HttpMethod, maxTimeout } from './constants'
|
||||
|
||||
const variableForTestSchema = z.object({
|
||||
id: z.string(),
|
||||
@ -46,6 +46,7 @@ export const webhookOptionsV5Schema = z.object({
|
||||
isCustomBody: z.boolean().optional(),
|
||||
isExecutedOnClient: z.boolean().optional(),
|
||||
webhook: webhookSchemas.v5.optional(),
|
||||
timeout: z.number().min(1).max(maxTimeout).optional(),
|
||||
})
|
||||
|
||||
const webhookOptionsSchemas = {
|
||||
|
Reference in New Issue
Block a user