This document describes the webhook processing system in the Sim platform, which enables workflows to be triggered by external events from 30+ providers including Slack, Microsoft Teams, Stripe, GitHub, WhatsApp, Telegram, and more. The system handles webhook reception, authentication, payload formatting, and execution queuing.
For information about general workflow execution, see Workflow Execution Engine. For deployment configuration and API endpoints, see Webhook & Trigger APIs.
The webhook processing system operates as a multi-stage pipeline with distinct phases for reception, validation, transformation, and execution.
Sources: apps/sim/app/api/webhooks/trigger/[path]/route.ts:37-176, apps/sim/lib/webhooks/processor.ts1-450
The webhook system accepts requests at /api/webhooks/trigger/[path] where [path] is a unique identifier assigned to each webhook during deployment.
| HTTP Method | Purpose | Handler |
|---|---|---|
| GET | Provider verification challenges (Microsoft Graph, WhatsApp) | route.ts24-35 |
| POST | Webhook event delivery | route.ts37-176 |
Sources: apps/sim/app/api/webhooks/trigger/[path]/route.ts:24-176
The parseWebhookBody function handles multiple content types:
The parser preserves the raw request body for signature verification while providing a parsed object for processing. Empty payloads are allowed (some providers send verification requests with no body).
Sources: apps/sim/lib/webhooks/processor.ts100-150
Many providers require URL verification before accepting webhook subscriptions. The system handles these challenges before body parsing to minimize latency.
Supported challenge types:
| Provider | Challenge Type | Parameter | Location |
|---|---|---|---|
| Microsoft Graph | Validation token | validationToken | Query string |
| Subscribe verification | hub.verify_token, hub.challenge | Query string | |
| Slack | URL verification | challenge field | Request body |
Sources: apps/sim/lib/webhooks/processor.ts152-185 apps/sim/lib/webhooks/utils.server.ts28-100
Webhooks are retrieved using findAllWebhooksForPath, which returns all webhooks matching a path to support credential set fan-out (multiple webhooks sharing one URL).
The query ensures webhooks are only active if:
isActive = truedeploymentVersionId matches the active deploymentSources: apps/sim/lib/webhooks/processor.ts377-418
When multiple webhooks match a path, the route processes each sequentially, continuing on failure:
Sources: apps/sim/app/api/webhooks/trigger/[path]/route.ts:76-176
The verifyProviderAuth function implements authentication for 15+ providers. Before verification, environment variables in providerConfig are resolved using resolveProviderConfigEnvVars.
Twilio Voice (HMAC-SHA1 with query parameters):
Sources: apps/sim/lib/webhooks/processor.ts455-798 apps/sim/lib/webhooks/utils.server.ts482-537
GitHub (supports SHA-1 and SHA-256):
The system prefers X-Hub-Signature-256 but falls back to X-Hub-Signature for legacy compatibility.
Sources: apps/sim/lib/webhooks/processor.ts743-768
Before execution, checkWebhookPreprocessing performs several checks via the unified preprocessExecution function:
Error responses:
| Status Code | Meaning | Action |
|---|---|---|
| 401 | Authentication failed | Return error immediately |
| 403 | Workflow not deployed / Usage limit exceeded | Return error immediately |
| 404 | Workflow not found | Return error immediately |
| 429 | Rate limit exceeded | Return error immediately |
| 500 | Internal error | Return error immediately |
Sources: apps/sim/lib/webhooks/processor.ts800-880 apps/sim/lib/execution/preprocessing.ts74-370
The shouldSkipWebhookEvent function implements event type filtering for providers that send multiple event types to a single endpoint.
Example: Stripe event filtering
A webhook configured with eventTypes: ['payment_intent.succeeded', 'charge.failed'] will reject events like customer.created.
Sources: apps/sim/lib/webhooks/processor.ts234-278
The formatWebhookInput function transforms raw webhook payloads into normalized structures for workflow execution. Each provider has custom formatting logic.
For Teams chat subscriptions, the system fetches the full message content from the Microsoft Graph API and downloads OneDrive/SharePoint attachments:
The system uses secureFetchWithPinnedIP to prevent SSRF attacks when downloading attachments from OneDrive share URLs.
Sources: apps/sim/lib/webhooks/utils.server.ts146-480
Slack file attachments are downloaded using the bot token with DNS pinning and size limits:
| Configuration | Value |
|---|---|
| Max file size | 50 MB |
| Max files per message | 15 |
| Security | validateUrlWithDNS + secureFetchWithPinnedIP |
| API fallback | Uses files.info if url_private is missing |
Sources: apps/sim/lib/webhooks/utils.server.ts539-689
Webhook executions are queued as background jobs to avoid blocking the HTTP response. The system supports both Trigger.dev (production) and inline execution (development).
Sources: apps/sim/lib/webhooks/processor.ts882-960 apps/sim/lib/core/async-jobs.ts
| Field | Type | Description |
|---|---|---|
webhookId | string | Webhook database record ID |
workflowId | string | Target workflow ID |
userId | string | Billing actor user ID |
provider | string | Provider identifier (slack, teams, etc.) |
body | any | Parsed webhook payload |
headers | Record<string, string> | HTTP headers |
path | string | Webhook path |
blockId? | string | Trigger block ID (for multi-trigger workflows) |
credentialId? | string | Credential used for this webhook |
credentialAccountUserId? | string | Account owner user ID |
Sources: apps/sim/background/webhook-execution.ts103-114
The executeWebhookJob function orchestrates the complete webhook execution lifecycle.
Sources: apps/sim/background/webhook-execution.ts116-664
Airtable webhooks require fetching the full record payload from the Airtable API (the webhook only includes record IDs):
Sources: apps/sim/background/webhook-execution.ts193-357 apps/sim/lib/webhooks/utils.server.ts1186-1359
For triggers with file or file[] outputs, the system uploads files to execution storage:
outputs schema for file fieldsWebhookAttachmentProcessorSources: apps/sim/background/webhook-execution.ts37-101
The system prevents duplicate webhook processing using provider-specific idempotency keys.
Idempotency storage:
Sources: apps/sim/background/webhook-execution.ts128-143 apps/sim/lib/core/idempotency/service.ts
Some providers require active subscription management (creating/renewing webhooks at the provider's API).
Sources: apps/sim/lib/webhooks/provider-subscriptions.ts1-500
Teams subscriptions expire after ~3 days and must be renewed. The system stores the externalSubscriptionId in providerConfig.
Sources: apps/sim/lib/webhooks/provider-subscriptions.ts64-203
Telegram requires calling setWebhook to register the webhook URL:
Sources: apps/sim/lib/webhooks/provider-subscriptions.ts440-540
For providers without push webhooks, the system implements polling services that run on a schedule.
Sources: apps/sim/lib/webhooks/rss-polling-service.ts120-235 apps/sim/lib/webhooks/imap-polling-service.ts1-400
The RSS polling service tracks seen items using:
Sources: apps/sim/lib/webhooks/rss-polling-service.ts120-235
The IMAP polling service connects to email servers and searches for unseen messages:
Sources: apps/sim/lib/webhooks/imap-polling-service.ts100-400
The system tracks consecutive failures and auto-disables webhooks after repeated errors.
| Constant | Value | Applied To |
|---|---|---|
MAX_CONSECUTIVE_FAILURES | 5 | Webhooks, Schedules, RSS, IMAP |
Sources: apps/sim/triggers/constants.ts1-3 apps/sim/lib/webhooks/rss-polling-service.ts73-104
Some providers require specific error response formats:
This ensures compatibility with Bot Framework's expected response structure.
Sources: apps/sim/lib/webhooks/processor.ts219-228
Certain providers (Grain) validate webhook URLs before workflows are deployed:
Sources: apps/sim/lib/webhooks/processor.ts281-295
Refresh this wiki