Skip to content

Commit f5f5589

Browse files
author
waleed
committed
feat(webhooks): added optioanl input format to webhooks, added support for file uploads
1 parent 4cd790b commit f5f5589

5 files changed

Lines changed: 373 additions & 4 deletions

File tree

apps/docs/content/docs/en/tools/generic_webhook.mdx

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,208 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
2626

2727

2828

29+
## Overview
30+
31+
The Generic Webhook block allows you to receive webhooks from any external service. This is a flexible trigger that can handle any JSON payload, making it ideal for integrating with services that don't have a dedicated Sim block.
32+
33+
## Basic Usage
34+
35+
### Simple Passthrough Mode
36+
37+
Without defining an input format, the webhook passes through the entire request body as-is:
38+
39+
```bash
40+
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
41+
-H "Content-Type: application/json" \
42+
-H "X-Sim-Secret: your-secret" \
43+
-d '{
44+
"message": "Test webhook trigger",
45+
"data": {
46+
"key": "value"
47+
}
48+
}'
49+
```
50+
51+
Access the data in downstream blocks using:
52+
- `<webhook1.message>` → "Test webhook trigger"
53+
- `<webhook1.data.key>` → "value"
54+
55+
### Structured Input Format (Optional)
56+
57+
Define an input schema to get typed fields and enable advanced features like file uploads:
58+
59+
**Input Format Configuration:**
60+
```json
61+
[
62+
{ "name": "message", "type": "string" },
63+
{ "name": "priority", "type": "number" },
64+
{ "name": "documents", "type": "files" }
65+
]
66+
```
67+
68+
**Webhook Request:**
69+
```bash
70+
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
71+
-H "Content-Type: application/json" \
72+
-H "X-Sim-Secret: your-secret" \
73+
-d '{
74+
"message": "Invoice submission",
75+
"priority": 1,
76+
"documents": [
77+
{
78+
"type": "file",
79+
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
80+
"name": "invoice.pdf",
81+
"mime": "application/pdf"
82+
}
83+
]
84+
}'
85+
```
86+
87+
## File Uploads
88+
89+
### Supported File Formats
90+
91+
The webhook supports two file input formats:
92+
93+
#### 1. Base64 Encoded Files
94+
For uploading file content directly:
95+
96+
```json
97+
{
98+
"documents": [
99+
{
100+
"type": "file",
101+
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
102+
"name": "screenshot.png",
103+
"mime": "image/png"
104+
}
105+
]
106+
}
107+
```
108+
109+
- **Max size**: 20MB per file
110+
- **Format**: Standard data URL with base64 encoding
111+
- **Storage**: Files are uploaded to secure execution storage
112+
113+
#### 2. URL References
114+
For passing existing file URLs:
115+
116+
```json
117+
{
118+
"documents": [
119+
{
120+
"type": "url",
121+
"data": "https://example.com/files/document.pdf",
122+
"name": "document.pdf",
123+
"mime": "application/pdf"
124+
}
125+
]
126+
}
127+
```
128+
129+
### Accessing Files in Downstream Blocks
130+
131+
Files are processed into `UserFile` objects with the following properties:
132+
133+
```typescript
134+
{
135+
id: string, // Unique file identifier
136+
name: string, // Original filename
137+
url: string, // Presigned URL (valid for 5 minutes)
138+
size: number, // File size in bytes
139+
type: string, // MIME type
140+
key: string, // Storage key
141+
uploadedAt: string, // ISO timestamp
142+
expiresAt: string // ISO timestamp (5 minutes)
143+
}
144+
```
145+
146+
**Access in blocks:**
147+
- `<webhook1.documents[0].url>` → Download URL
148+
- `<webhook1.documents[0].name>` → "invoice.pdf"
149+
- `<webhook1.documents[0].size>` → 524288
150+
- `<webhook1.documents[0].type>` → "application/pdf"
151+
152+
### Complete File Upload Example
153+
154+
```bash
155+
# Create a base64-encoded file
156+
echo "Hello World" | base64
157+
# SGVsbG8gV29ybGQK
158+
159+
# Send webhook with file
160+
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
161+
-H "Content-Type: application/json" \
162+
-H "X-Sim-Secret: your-secret" \
163+
-d '{
164+
"subject": "Document for review",
165+
"attachments": [
166+
{
167+
"type": "file",
168+
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
169+
"name": "sample.txt",
170+
"mime": "text/plain"
171+
}
172+
]
173+
}'
174+
```
175+
176+
## Authentication
177+
178+
### Configure Authentication (Optional)
179+
180+
In the webhook configuration:
181+
1. Enable "Require Authentication"
182+
2. Set a secret token
183+
3. Choose header type:
184+
- **Custom Header**: `X-Sim-Secret: your-token`
185+
- **Authorization Bearer**: `Authorization: Bearer your-token`
186+
187+
### Using Authentication
188+
189+
```bash
190+
# With custom header
191+
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
192+
-H "Content-Type: application/json" \
193+
-H "X-Sim-Secret: your-secret-token" \
194+
-d '{"message": "Authenticated request"}'
195+
196+
# With bearer token
197+
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
198+
-H "Content-Type: application/json" \
199+
-H "Authorization: Bearer your-secret-token" \
200+
-d '{"message": "Authenticated request"}'
201+
```
202+
203+
## Best Practices
204+
205+
1. **Use Input Format for Structure**: Define an input format when you know the expected schema. This provides:
206+
- Type validation
207+
- Better autocomplete in the editor
208+
- File upload capabilities
209+
210+
2. **Authentication**: Always enable authentication for production webhooks to prevent unauthorized access.
211+
212+
3. **File Size Limits**: Keep files under 20MB. For larger files, use URL references instead.
213+
214+
4. **File Expiration**: Downloaded files have 5-minute expiration URLs. Process them promptly or store them elsewhere if needed longer.
215+
216+
5. **Error Handling**: Webhook processing is asynchronous. Check execution logs for errors.
217+
218+
6. **Testing**: Use the "Test Webhook" button in the editor to validate your configuration before deployment.
219+
220+
## Use Cases
221+
222+
- **Form Submissions**: Receive data from custom forms with file uploads
223+
- **Third-Party Integrations**: Connect with services that send webhooks (Stripe, GitHub, etc.)
224+
- **Document Processing**: Accept documents from external systems for processing
225+
- **Event Notifications**: Receive event data from various sources
226+
- **Custom APIs**: Build custom API endpoints for your applications
227+
29228
## Notes
30229

31230
- Category: `triggers`
32231
- Type: `generic_webhook`
232+
- **File Support**: Available via input format configuration
233+
- **Max File Size**: 20MB per file

apps/sim/background/webhook-execution.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import { LoggingSession } from '@/lib/logs/execution/logging-session'
1111
import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans'
1212
import { decryptSecret } from '@/lib/utils'
1313
import { WebhookAttachmentProcessor } from '@/lib/webhooks/attachment-processor'
14-
import { fetchAndProcessAirtablePayloads, formatWebhookInput } from '@/lib/webhooks/utils'
14+
import {
15+
fetchAndProcessAirtablePayloads,
16+
formatWebhookInput,
17+
processGenericWebhookFiles,
18+
} from '@/lib/webhooks/utils'
1519
import {
1620
loadDeployedWorkflowState,
1721
loadWorkflowFromNormalizedTables,
@@ -410,6 +414,52 @@ async function executeWebhookJobInternal(
410414
}
411415
}
412416

417+
// Process generic webhook files based on inputFormat
418+
if (input && payload.provider === 'generic' && payload.blockId && blocks[payload.blockId]) {
419+
try {
420+
const triggerBlock = blocks[payload.blockId]
421+
422+
if (triggerBlock?.subBlocks?.inputFormat?.value) {
423+
const inputFormat = triggerBlock.subBlocks.inputFormat.value as unknown as Array<{
424+
name: string
425+
type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'files'
426+
}>
427+
logger.debug(`[${requestId}] Processing generic webhook files from inputFormat`)
428+
429+
const fileFields = inputFormat.filter((field) => field.type === 'files')
430+
431+
if (fileFields.length > 0 && typeof input === 'object' && input !== null) {
432+
const executionContext = {
433+
workspaceId: workspaceId || '',
434+
workflowId: payload.workflowId,
435+
}
436+
437+
for (const fileField of fileFields) {
438+
const fieldValue = input[fileField.name]
439+
440+
if (fieldValue && typeof fieldValue === 'object') {
441+
const uploadedFiles = await processGenericWebhookFiles(
442+
fieldValue,
443+
executionContext,
444+
requestId
445+
)
446+
447+
if (uploadedFiles.length > 0) {
448+
input[fileField.name] = uploadedFiles
449+
logger.info(
450+
`[${requestId}] Successfully processed ${uploadedFiles.length} file(s) for field: ${fileField.name}`
451+
)
452+
}
453+
}
454+
}
455+
}
456+
}
457+
} catch (error) {
458+
logger.error(`[${requestId}] Error processing generic webhook files:`, error)
459+
// Continue without processing files rather than failing execution
460+
}
461+
}
462+
413463
// Create executor and execute
414464
const executor = new Executor({
415465
workflow: serializedWorkflow,

apps/sim/blocks/blocks/generic_webhook.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ export const GenericWebhookBlock: BlockConfig = {
2828
triggerProvider: 'generic',
2929
availableTriggers: ['generic_webhook'],
3030
},
31+
// Optional input format for structured data including files
32+
{
33+
id: 'inputFormat',
34+
title: 'Input Format',
35+
type: 'input-format',
36+
layout: 'full',
37+
description:
38+
'Define the expected JSON input schema for this webhook (optional). Use type "files" for file uploads.',
39+
},
3140
],
3241

3342
tools: {

0 commit comments

Comments
 (0)