Triggers and Webhook Handlers for Event-Driven Workflows in simstudioai/sim

The simstudioai/sim repository implements event-driven workflows through a modular trigger system where each integration lives in apps/sim/triggers/<service>/ and exposes a standardized webhook handler that validates external payloads, maps them to normalized TriggerEvent objects, and enqueues workflow executions via the runTrigger() function.

The simstudioai/sim platform provides a comprehensive event-driven workflow architecture that converts external SaaS events into automated workflow executions. This system organizes triggers and webhook handlers within a predictable directory structure under apps/sim/triggers/, enabling integrations with services ranging from GitHub to Stripe. Understanding the trigger lifecycle—from incoming webhook validation to execution enqueueing—is essential for developers extending or debugging the platform's event handling capabilities.

Trigger Architecture and Directory Structure

Each trigger integration follows a consistent four-file pattern within apps/sim/triggers/<service>/:

  • index.ts: Registers the trigger with the platform, exposing the trigger ID, display name, UI configuration schema, and supported event types.
  • webhook.ts: Implements the HTTP POST handler that receives third-party webhooks, validates request signatures, and initiates workflow executions.
  • event-*.ts: Service-specific event converters that map raw webhook payloads to the normalized TriggerEvent interface consumed by the executor.
  • utils.ts: Helper functions for signature verification, payload normalization, and generating UI instructions for webhook URL configuration.

All webhook handlers are registered as Next.js API routes in apps/sim/app/api/v1/triggers/<service>/route.ts, which simply forwards incoming requests to the respective service's webhook.ts handler.

Catalog of Built-in Triggers

The platform supports webhook-based triggers for real-time events and polling-based triggers for services without native webhook support.

Webhook-Based Triggers

These services push events to the platform via HTTP POST requests:

Additional webhook integrations include Asana, Ashby, Attio, Cal.com, CircleBack, Fathom, Fireflies, Google Forms, Gong, JSM (Jira Service Management), Intercom, Lemlist, Notion, Pipedrive, RingCentral, Telegram, Twilio Voice, Vercel, and WhatsApp.

Polling-Based Triggers

For services lacking webhook capabilities, the platform uses polling handlers located in poller.ts files:

How Webhook Handlers Process Events

Every webhook handler in simstudioai/sim follows a standardized execution flow. The GitHub integration provides a canonical example of this pattern.

1. Route Registration and Entry Point

Incoming requests hit apps/sim/app/api/v1/triggers/github/route.ts, which forwards the POST request to apps/sim/triggers/github/webhook.ts. This thin routing layer allows the platform to leverage Next.js API route conventions while keeping business logic organized in the triggers directory.

2. Signature Verification

The handler first validates the request authenticity using service-specific logic in apps/sim/triggers/github/utils.ts. The verifySignature(payload, signatureHeader) function verifies the HMAC signature provided in the x-hub-signature-256 header against the configured webhook secret.

3. Event Routing and Normalization

After validation, the handler extracts the event type from the x-github-event header and routes to the appropriate event converter file:

// Example from apps/sim/triggers/github/webhook.ts
const eventName = request.headers.get('x-github-event');
const { toTriggerEvent } = await import(`./event-${eventName}.ts`);
const triggerEvent = toTriggerEvent(payload);

Individual event files like apps/sim/triggers/github/push.ts and apps/sim/triggers/github/issue_opened.ts implement toTriggerEvent(payload): TriggerEvent, normalizing disparate payload structures into the platform's standard event schema.

4. Execution Enqueueing

Finally, the normalized event passes to runTrigger(event), defined in apps/sim/lib/triggers/runner.ts. This function creates a workflow execution record in the database and enqueues the job for processing by the executor service. The webhook handler returns the job ID to the caller:

const job = await runTrigger(triggerEvent);
return NextResponse.json({ ok: true, jobId: job.id });

Implementing a Custom Trigger

To add a new service integration, create the standard four-file structure. Below is a complete skeleton for a hypothetical myservice integration:

// apps/sim/triggers/myservice/index.ts
import type { TriggerConfig } from '@/triggers/types';
import { webhook } from './webhook';
import { MyServiceIcon } from '@/components/icons';

export const myserviceTrigger: TriggerConfig = {
  id: 'myservice',
  name: 'My Service',
  description: 'Handles events from My Service',
  icon: MyServiceIcon,
  webhook,
  events: ['event_a', 'event_b'],
};
// apps/sim/triggers/myservice/webhook.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifySignature } from './utils';
import { toEventA } from './event_a';
import { toEventB } from './event_b';
import { runTrigger } from '@/lib/triggers/runner';

export async function POST(request: NextRequest) {
  const raw = await request.text();
  
  if (!verifySignature(raw, request.headers.get('x-myservice-signature'))) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
  }
  
  const payload = JSON.parse(raw);
  const eventName = request.headers.get('x-myservice-event')!;

  const triggerEvent =
    eventName === 'event_a' ? toEventA(payload) :
    eventName === 'event_b' ? toEventB(payload) :
    null;

  if (!triggerEvent) {
    return NextResponse.json({ error: 'Unsupported event' }, { status: 400 });
  }

  const job = await runTrigger(triggerEvent);
  return NextResponse.json({ ok: true, jobId: job.id });
}

Key Files Reference

Understanding the following files is critical for working with triggers and webhook handlers in simstudioai/sim:

  • apps/sim/triggers/<service>/index.ts: Declares trigger metadata and UI configuration.
  • apps/sim/triggers/<service>/webhook.ts: Core HTTP handler implementing validation and event processing.
  • apps/sim/triggers/<service>/utils.ts: Signature verification and payload helpers.
  • apps/sim/triggers/<service>/event-*.ts: Normalization functions converting service payloads to TriggerEvent.
  • apps/sim/lib/triggers/runner.ts: Central execution routine that persists and enqueues workflow jobs.
  • apps/sim/app/api/v1/triggers/<service>/route.ts: Next.js API route forwarding layer.

Summary

  • Triggers in simstudioai/sim reside in apps/sim/triggers/<service>/ and follow a standardized four-file architecture: index.ts, webhook.ts, event-*.ts, and utils.ts.
  • Webhook handlers validate incoming requests using service-specific signature verification, normalize payloads into TriggerEvent objects, and invoke runTrigger() from apps/sim/lib/triggers/runner.ts to enqueue workflow executions.
  • Polling triggers like Google Calendar and RSS use poller.ts implementations instead of webhooks to detect changes.
  • Route registration occurs in apps/sim/app/api/v1/triggers/<service>/route.ts, which forwards requests to the respective service's webhook handler.
  • All event converters implement a toTriggerEvent() function that ensures external payloads conform to the platform's internal event schema.

Frequently Asked Questions

How do I add a new webhook trigger to simstudioai/sim?

Create a directory under apps/sim/triggers/<service>/ containing index.ts for configuration, webhook.ts for the HTTP handler, and individual event-*.ts files for each event type you support. Implement signature verification in utils.ts using the service's documented method, then register the route in apps/sim/app/api/v1/triggers/<service>/route.ts that imports your webhook handler.

What is the difference between webhook and polling triggers in this repository?

Webhook triggers receive real-time HTTP POST requests from external services to endpoints like /api/v1/triggers/github, requiring immediate validation and response. Polling triggers, such as those for Google Calendar in apps/sim/triggers/google-calendar/poller.ts, periodically query external APIs to detect changes and do not require public webhook endpoints.

How does the platform validate webhook signatures?

Each service implements a verifySignature() function in its utils.ts file. For example, the GitHub trigger uses HMAC-SHA256 verification against the x-hub-signature-256 header, while Stripe uses timestamped signatures to prevent replay attacks. The webhook handler rejects requests with invalid signatures before processing the payload.

Where are workflow executions queued after a trigger fires?

After validation and normalization, the runTrigger() function in apps/sim/lib/triggers/runner.ts creates a workflow execution record in the database and enqueues the job for the executor service. This function returns a job ID that the webhook handler includes in its HTTP response to the calling service.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →