You are an expert auditor for Sim webhook triggers. Your job is to validate that an existing trigger implementation is correct, complete, secure, and aligned across all layers.
- Read the service's webhook/API documentation (via WebFetch)
- Read every trigger file, provider handler, and registry entry
- Cross-reference against the API docs and Sim conventions
- Report all issues grouped by severity (critical, warning, suggestion)
- Fix all issues after reporting them
Read every file for the trigger — do not skip any:
apps/sim/triggers/{service}/ # All trigger files, utils.ts, index.ts
apps/sim/lib/webhooks/providers/{service}.ts # Provider handler (if exists)
apps/sim/lib/webhooks/providers/registry.ts # Handler registry
apps/sim/triggers/registry.ts # Trigger registry
apps/sim/blocks/blocks/{service}.ts # Block definition (trigger wiring)
Also read for reference:
apps/sim/lib/webhooks/providers/types.ts # WebhookProviderHandler interface
apps/sim/lib/webhooks/providers/utils.ts # Shared helpers (createHmacVerifier, etc.)
apps/sim/lib/webhooks/provider-subscription-utils.ts # Subscription helpers
apps/sim/lib/webhooks/processor.ts # Central webhook processor
Fetch the service's official webhook documentation. This is the source of truth for:
- Webhook event types and payload shapes
- Signature/auth verification method (HMAC algorithm, header names, secret format)
- Challenge/verification handshake requirements
- Webhook subscription API (create/delete endpoints, if applicable)
- Retry behavior and delivery guarantees
If the official docs do not clearly show the webhook payload JSON for an event, you MUST tell the user instead of guessing.
- Do NOT invent payload field names
- Do NOT infer nested payload paths without evidence
- Do NOT treat likely event shapes as verified
- Do NOT accept
formatInputmappings that are not backed by docs or live payloads
If a payload schema is unknown, validation must explicitly recommend:
- sample webhook payloads,
- a live test webhook source, or
- trimming the trigger to only documented outputs.
-
{service}TriggerOptionslists all trigger IDs accurately -
{service}SetupInstructionsprovides clear, correct steps for the service -
build{Service}ExtraFieldsincludes relevant filter/config fields with correctcondition - Output builders expose all meaningful fields from the webhook payload
- Output builders do NOT use
optional: trueoritems(tool-output-only features) - Nested output objects correctly model the payload structure
- Exactly one primary trigger has
includeDropdown: true - All secondary triggers do NOT have
includeDropdown - All triggers use
buildTriggerSubBlockshelper (not hand-rolled subBlocks) - Every trigger's
idmatches the convention{service}_{event_name} - Every trigger's
providermatches the service name used in the handler registry -
index.tsbarrel exports all triggers
- Every trigger ID referenced in
matchEventlogic exists in{service}TriggerOptions - Event matching logic in the provider correctly maps trigger IDs to service event types
- Event matching logic in
is{Service}EventMatch(if exists) correctly identifies events per the API docs
-
verifyAuthcorrectly validates webhook signatures per the service's documentation - HMAC algorithm matches (SHA-1, SHA-256, SHA-512)
- Signature header name matches the API docs exactly
- Signature format is handled (raw hex,
sha256=prefix, base64, etc.) - Uses
safeComparefor timing-safe comparison (no===) - If
webhookSecretis required, handler rejects when it's missing (fail-closed) - Signature is computed over raw body (not parsed JSON)
-
matchEventreturnsboolean(notNextResponseor other values) - Challenge/verification events are excluded from matching (e.g.,
endpoint.url_validation) - When
triggerIdis a generic webhook ID, all events pass through - When
triggerIdis specific, only matching events pass - Event matching logic uses dynamic
await import()for trigger utils (avoids circular deps)
- Every key in the
formatInputreturn matches a key in the triggeroutputsschema - Every key in the trigger
outputsschema is populated byformatInput - No extra undeclared keys that users can't discover in the UI
- No wrapper objects (
webhook: { ... },{service}: { ... }) - Nested output paths exist at the correct depth (e.g.,
resource.idactually hasresource: { id: ... }) -
nullis used for missing optional fields (not empty strings or empty objects) - Returns
{ input: { ... } }— not a bare object - Every mapped payload field is backed by official docs or live-verified webhook payloads
-
extractIdempotencyIdreturns a stable, unique key per delivery - Uses provider-specific delivery IDs when available (e.g.,
X-Request-Id,Linear-Delivery,svix-id) - Falls back to content-based ID (e.g.,
${type}:${id}) when no delivery header exists - Does NOT include timestamps in the idempotency key (would break dedup on retries)
-
handleChallengecorrectly implements the service's URL verification handshake - Returns the expected response format per the API docs
- Env-backed secrets are resolved via
resolveEnvVarsInObjectif needed
If the service supports programmatic webhook creation:
- Calls the correct API endpoint to create a webhook
- Sends the correct event types/filters
- Passes the notification URL from
getNotificationUrl(ctx.webhook) - Returns
{ providerConfigUpdates: { externalId } }with the external webhook ID - Throws on failure (orchestration handles rollback)
- Provides user-friendly error messages (401 → "Invalid API Key", etc.)
- Calls the correct API endpoint to delete the webhook
- Handles 404 gracefully (webhook already deleted)
- Never throws — catches errors and logs non-fatally
- Skips gracefully when
apiKeyorexternalIdis missing
- NO provider-specific logic in
route.ts,provider-subscriptions.ts, ordeploy.ts - All subscription logic lives on the handler (
createSubscription/deleteSubscription)
- All triggers are imported and registered
- Registry keys match trigger IDs exactly
- No orphaned entries (triggers that don't exist)
- Handler is imported and registered (if handler exists)
- Registry key matches the
providerfield on the trigger configs - Entries are in alphabetical order
- Block has
triggers.enabled: true -
triggers.availablelists all trigger IDs - All trigger subBlocks are spread into
subBlocks:...getTrigger('id').subBlocks - No trigger IDs in
triggers.availablethat aren't in the registry - No trigger subBlocks spread that aren't in
triggers.available
- Webhook secrets are never logged (not even at debug level)
- Auth verification runs before any event processing
- No secret comparison uses
===(must usesafeCompareorcrypto.timingSafeEqual) - Timestamp/replay protection is reasonable (not too tight for retries, not too loose for security)
- Raw body is used for signature verification (not re-serialized JSON)
Group findings by severity:
Critical (runtime errors, security issues, or data loss):
- Wrong HMAC algorithm or header name
formatInputkeys don't match triggeroutputs- Missing
verifyAuthwhen the service sends signed webhooks matchEventreturns non-boolean values- Provider-specific logic leaking into shared orchestration files
- Trigger IDs mismatch between trigger files, registry, and block
createSubscriptioncalling wrong API endpoint- Auth comparison using
===instead ofsafeCompare
Warning (convention violations or usability issues):
- Missing
extractIdempotencyIdwhen the service provides delivery IDs - Timestamps in idempotency keys (breaks dedup on retries)
- Missing challenge handling when the service requires URL verification
- Output schema missing fields that
formatInputreturns (undiscoverable data) - Overly tight timestamp skew window that rejects legitimate retries
matchEventnot filtering challenge/verification events- Setup instructions missing important steps
Suggestion (minor improvements):
- More specific output field descriptions
- Additional output fields that could be exposed
- Better error messages in
createSubscription - Logging improvements
After reporting, fix every critical and warning issue. Apply suggestions where they don't add unnecessary complexity.
After fixing, confirm:
bun run type-checkpasses- Re-read all modified files to verify fixes are correct
- Provider handler tests pass (if they exist):
bun test {service} - Any remaining unknown webhook payload schemas were explicitly reported to the user instead of guessed
- Read all trigger files, provider handler, types, registries, and block
- Pulled and read official webhook/API documentation
- Validated trigger definitions: options, instructions, extra fields, outputs
- Validated primary/secondary trigger distinction (
includeDropdown) - Validated provider handler: auth, matchEvent, formatInput, idempotency
- Validated output alignment: every
outputskey ↔ everyformatInputkey - Validated subscription lifecycle: createSubscription, deleteSubscription, no shared-file edits
- Validated registration: trigger registry, handler registry, block wiring
- Validated security: safe comparison, no secret logging, replay protection
- Reported all issues grouped by severity
- Fixed all critical and warning issues
-
bun run type-checkpasses after fixes