Back to guides
1
5 min

Event System

Webhook Receivers, Event Bus & Schema Validation

Why Events?

Every automation starts with a trigger. A customer submits a support ticket. A payment processes. A document uploads. A CI pipeline fails. These are events — discrete signals that something happened in the outside world.

The event system is the front door of your automation pipeline. It receives these signals (usually via webhooks), validates them, normalizes them into a consistent shape, and routes them to the right workflow. Get this wrong and everything downstream is unreliable.

The principle: Normalize early, validate strictly, route loosely.

Webhook Normalization

External services send webhooks in different formats. Stripe sends JSON with nested objects. Zendesk sends flat payloads. GitHub sends different schemas per event type. Your system shouldn't care about these differences.

The webhook receiver converts every incoming payload into a standard WorkflowEvent:

interface WorkflowEvent {
  id: string;          // UUID — unique per event
  type: string;        // "support_ticket.created", "payment.completed"
  source: EventSource; // "webhook" | "cron" | "manual" | "internal"
  priority: EventPriority; // inferred from content
  payload: Record<string, unknown>; // the actual data
  metadata: EventMetadata; // correlation ID, trace ID, retry count
  timestamp: string;
}

Every downstream component — nodes, engine, reliability — speaks this language. They never see the raw Stripe or Zendesk format.

Schema Validation

Not every webhook payload is valid. A support ticket without a customer_email will break the response generator. A payment without an amount will break cost tracking.

Schema validation catches these problems at the front door:

registerSchema({
  type: "support_ticket.created",
  version: "1.0",
  required: ["ticket_id", "subject", "body", "customer_email"],
  properties: {
    ticket_id: { type: "string", description: "Unique ticket identifier" },
    subject: { type: "string", description: "Ticket subject line" },
    // ...
  },
});

Events that fail validation are rejected before entering the pipeline. This is fail fast — the earlier you catch a bad event, the less damage it does.

The Event Bus

The event bus decouples producers from consumers. The webhook receiver publishes events. Workflows subscribe. Neither knows about the other.

// Subscribe to specific event types
eventBus.subscribe("support_ticket.created", async (event) => {
  await triageWorkflow.execute(event);
});

// Subscribe to all events (monitoring, audit logging)
eventBus.subscribe("*", async (event) => {
  auditTrail.log("system", "event.received", event.id);
});

Key design: handlers execute concurrently via Promise.allSettled(). One failing handler doesn't block others. If the billing workflow crashes, the support workflow still runs.

Rate Limiting

Webhook storms happen. A misconfigured integration sends the same event 1,000 times. A batch operation triggers 10,000 webhooks in a minute. Without rate limiting, your system drowns.

The receiver uses a sliding window counter per source. Requests beyond the limit (e.g., 100/minute for Zendesk) are rejected immediately with a clear error. This protects every downstream component — nodes, engine, database, AI API.

What's Next

In Module 2, you'll build the AI nodes that process these events — classifying tickets by category and urgency, extracting entities, generating draft responses. The event system feeds them a clean, validated, consistent stream of data.

This is chapter 1 of AI Workflow Automation.

Get the full hands-on course — free during early access. Build the complete system. Your projects become your portfolio.

View course details