Skip to content

Commit dbde59b

Browse files
committed
Add webhook block
1 parent c2764f4 commit dbde59b

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ const BUILT_IN_TOOL_TYPES = new Set([
788788
'tts',
789789
'stt',
790790
'memory',
791+
'webhook_request',
791792
'workflow',
792793
])
793794

@@ -916,6 +917,7 @@ export function ToolInput({
916917
(block) =>
917918
(block.category === 'tools' ||
918919
block.type === 'api' ||
920+
block.type === 'webhook_request' ||
919921
block.type === 'workflow' ||
920922
block.type === 'knowledge' ||
921923
block.type === 'function') &&
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { createHmac } from 'crypto'
2+
import { createLogger } from '@sim/logger'
3+
import { v4 as uuidv4 } from 'uuid'
4+
import { WebhookIcon } from '@/components/icons'
5+
import type { BlockConfig } from '@/blocks/types'
6+
import type { RequestResponse } from '@/tools/http/types'
7+
8+
const logger = createLogger('WebhookRequestBlock')
9+
10+
/**
11+
* Generates HMAC-SHA256 signature for webhook payload
12+
*/
13+
function generateSignature(secret: string, timestamp: number, body: string): string {
14+
const signatureBase = `${timestamp}.${body}`
15+
return createHmac('sha256', secret).update(signatureBase).digest('hex')
16+
}
17+
18+
export const WebhookRequestBlock: BlockConfig<RequestResponse> = {
19+
type: 'webhook_request',
20+
name: 'Webhook',
21+
description: 'Send a webhook request',
22+
longDescription:
23+
'Send an HTTP POST request to a webhook URL with automatic webhook headers. Optionally sign the payload with HMAC-SHA256 for secure webhook delivery.',
24+
docsLink: 'https://docs.sim.ai/blocks/webhook',
25+
category: 'blocks',
26+
bgColor: '#10B981',
27+
icon: WebhookIcon,
28+
subBlocks: [
29+
{
30+
id: 'url',
31+
title: 'Webhook URL',
32+
type: 'short-input',
33+
placeholder: 'https://example.com/webhook',
34+
required: true,
35+
},
36+
{
37+
id: 'body',
38+
title: 'Payload',
39+
type: 'code',
40+
placeholder: 'Enter JSON payload...',
41+
language: 'json',
42+
wandConfig: {
43+
enabled: true,
44+
maintainHistory: true,
45+
prompt: `You are an expert JSON programmer.
46+
Generate ONLY the raw JSON object based on the user's request.
47+
The output MUST be a single, valid JSON object, starting with { and ending with }.
48+
49+
Current payload: {context}
50+
51+
Do not include any explanations, markdown formatting, or other text outside the JSON object.
52+
53+
You have access to the following variables you can use to generate the JSON payload:
54+
- Use angle brackets for workflow variables, e.g., '<blockName.output>'.
55+
- Use double curly braces for environment variables, e.g., '{{ENV_VAR_NAME}}'.
56+
57+
Example:
58+
{
59+
"event": "workflow.completed",
60+
"data": {
61+
"result": "<agent.content>",
62+
"timestamp": "<function.result>"
63+
}
64+
}`,
65+
placeholder: 'Describe the webhook payload you need...',
66+
generationType: 'json-object',
67+
},
68+
},
69+
{
70+
id: 'secret',
71+
title: 'Signing Secret',
72+
type: 'short-input',
73+
placeholder: 'Optional: Secret for HMAC signature',
74+
password: true,
75+
connectionDroppable: false,
76+
},
77+
{
78+
id: 'headers',
79+
title: 'Additional Headers',
80+
type: 'table',
81+
columns: ['Key', 'Value'],
82+
description: 'Optional custom headers to include with the webhook request',
83+
},
84+
],
85+
tools: {
86+
access: ['http_request'],
87+
config: {
88+
tool: () => 'http_request',
89+
params: (params: Record<string, any>) => {
90+
const timestamp = Date.now()
91+
const deliveryId = uuidv4()
92+
93+
// Start with webhook-specific headers
94+
const webhookHeaders: Record<string, string> = {
95+
'Content-Type': 'application/json',
96+
'X-Webhook-Timestamp': timestamp.toString(),
97+
'X-Delivery-ID': deliveryId,
98+
'Idempotency-Key': deliveryId,
99+
}
100+
101+
// Add signature if secret is provided
102+
if (params.secret) {
103+
const bodyString =
104+
typeof params.body === 'string' ? params.body : JSON.stringify(params.body || {})
105+
const signature = generateSignature(params.secret, timestamp, bodyString)
106+
webhookHeaders['X-Webhook-Signature'] = `t=${timestamp},v1=${signature}`
107+
}
108+
109+
// Merge with user-provided headers (user headers take precedence)
110+
// Headers must be in TableRow format: { cells: { Key: string, Value: string } }
111+
const userHeaders = params.headers || []
112+
const mergedHeaders = [
113+
...Object.entries(webhookHeaders).map(([key, value]) => ({
114+
cells: { Key: key, Value: value },
115+
})),
116+
...userHeaders,
117+
]
118+
119+
const payload = {
120+
url: params.url,
121+
method: 'POST',
122+
headers: mergedHeaders,
123+
body: params.body,
124+
}
125+
126+
logger.info('Sending webhook request', {
127+
url: payload.url,
128+
method: payload.method,
129+
headers: mergedHeaders,
130+
body: payload.body,
131+
hasSignature: !!params.secret,
132+
})
133+
134+
return payload
135+
},
136+
},
137+
},
138+
inputs: {
139+
url: { type: 'string', description: 'Webhook URL to send the request to' },
140+
body: { type: 'json', description: 'JSON payload to send' },
141+
secret: { type: 'string', description: 'Optional secret for HMAC-SHA256 signature' },
142+
headers: { type: 'json', description: 'Optional additional headers' },
143+
},
144+
outputs: {
145+
data: { type: 'json', description: 'Response data from the webhook endpoint' },
146+
status: { type: 'number', description: 'HTTP status code' },
147+
headers: { type: 'json', description: 'Response headers' },
148+
},
149+
}
150+

apps/sim/blocks/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ import { WaitBlock } from '@/blocks/blocks/wait'
129129
import { WealthboxBlock } from '@/blocks/blocks/wealthbox'
130130
import { WebflowBlock } from '@/blocks/blocks/webflow'
131131
import { WebhookBlock } from '@/blocks/blocks/webhook'
132+
import { WebhookRequestBlock } from '@/blocks/blocks/webhook_request'
132133
import { WhatsAppBlock } from '@/blocks/blocks/whatsapp'
133134
import { WikipediaBlock } from '@/blocks/blocks/wikipedia'
134135
import { WordPressBlock } from '@/blocks/blocks/wordpress'
@@ -276,6 +277,7 @@ export const registry: Record<string, BlockConfig> = {
276277
wealthbox: WealthboxBlock,
277278
webflow: WebflowBlock,
278279
webhook: WebhookBlock,
280+
webhook_request: WebhookRequestBlock,
279281
whatsapp: WhatsAppBlock,
280282
wikipedia: WikipediaBlock,
281283
wordpress: WordPressBlock,

0 commit comments

Comments
 (0)