Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tailor-platform/sdk/llms.txt

Use this file to discover all available pages before exploring further.

Webhook triggers allow your Tailor Platform application to receive and process HTTP requests from external services, enabling integrations with third-party platforms.

Overview

The incomingWebhookTrigger() function creates an endpoint that can receive HTTP requests. When a request arrives, the executor processes the webhook payload and can perform any operation in response.

Parameters

The trigger accepts a generic type parameter to specify the expected webhook structure:
incomingWebhookTrigger<T extends IncomingWebhookRequest>()
T
IncomingWebhookRequest
Type definition for the incoming webhook request structure.
interface IncomingWebhookRequest {
  body: Record<string, unknown>;
  headers: Record<string, string>;
}

Basic Example

import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";

export default createExecutor({
  name: "test-webhook",
  description: "Test executor for incoming webhook trigger",
  trigger: incomingWebhookTrigger<{
    body: { message: string };
    headers: Record<string, string>;
  }>(),
  operation: {
    kind: "function",
    body: (args) => {
      console.log("Webhook received:", args.body);
      console.log("Headers:", args.headers);
    },
  },
});

Event Context

Webhook triggers receive comprehensive HTTP request information:
interface IncomingWebhookArgs<T> {
  body: T["body"];                      // Parsed request body
  headers: T["headers"];                 // Request headers
  method: "POST" | "GET" | "PUT" | "DELETE"; // HTTP method
  rawBody: string;                       // Raw request body as string
  env: TailorEnv;                        // Environment information
}

Accessing Request Data

export default createExecutor({
  name: "webhook-handler",
  trigger: incomingWebhookTrigger<{
    body: { event: string; data: unknown };
    headers: { "x-signature": string };
  }>(),
  operation: {
    kind: "function",
    body: async ({ body, headers, method, rawBody }) => {
      console.log(`Received ${method} request`);
      console.log("Event:", body.event);
      console.log("Signature:", headers["x-signature"]);
      console.log("Raw body:", rawBody);
    },
  },
});

Real-World Examples

Stripe Webhook Handler

import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";
import { getDB } from "../generated/tailordb";

interface StripeWebhook {
  type: string;
  data: { 
    object: { 
      id: string; 
      amount: number;
      customer: string;
    } 
  };
}

export default createExecutor({
  name: "stripe-webhook",
  description: "Handle Stripe payment events",
  trigger: incomingWebhookTrigger<{
    body: StripeWebhook;
    headers: { "stripe-signature": string };
  }>(),
  operation: {
    kind: "function",
    body: async ({ body, headers }) => {
      const signature = headers["stripe-signature"];
      
      // Verify webhook signature
      // (In production, use Stripe's SDK to verify)
      
      if (body.type === "payment_intent.succeeded") {
        const db = getDB("tailordb");
        
        await db
          .insertInto("Payment")
          .values({
            stripePaymentId: body.data.object.id,
            amount: body.data.object.amount,
            customerId: body.data.object.customer,
            status: "succeeded",
          })
          .execute();
      }
    },
  },
});

GitHub Webhook Integration

import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";

interface GitHubWebhook {
  action: "opened" | "closed" | "reopened";
  pull_request: {
    id: number;
    title: string;
    user: { login: string };
    html_url: string;
  };
}

export default createExecutor({
  name: "github-pr-webhook",
  description: "Handle GitHub pull request events",
  trigger: incomingWebhookTrigger<{
    body: GitHubWebhook;
    headers: { "x-github-event": string };
  }>(),
  operation: {
    kind: "function",
    body: async ({ body, headers }) => {
      const event = headers["x-github-event"];
      
      if (event === "pull_request" && body.action === "opened") {
        console.log(`New PR: ${body.pull_request.title}`);
        console.log(`Author: ${body.pull_request.user.login}`);
        console.log(`URL: ${body.pull_request.html_url}`);
        
        // Send notification to team
      }
    },
  },
});

Generic API Webhook

import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";

export default createExecutor({
  name: "api-webhook",
  description: "Generic webhook endpoint",
  trigger: incomingWebhookTrigger<{
    body: Record<string, unknown>;
    headers: Record<string, string>;
  }>(),
  operation: {
    kind: "function",
    body: async ({ body, method, headers }) => {
      // Log all incoming webhooks
      console.log({
        timestamp: new Date().toISOString(),
        method,
        body,
        headers,
      });
      
      // Process based on content
      if (body.action === "notify") {
        // Send notification
      }
    },
  },
});

Security Considerations

Verify Webhook Signatures

Always verify webhook signatures from trusted sources:
import crypto from "crypto";

body: async ({ body, headers, rawBody }) => {
  const signature = headers["x-webhook-signature"];
  const secret = process.env.WEBHOOK_SECRET;
  
  // Verify HMAC signature
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  
  if (signature !== expectedSignature) {
    throw new Error("Invalid webhook signature");
  }
  
  // Process verified webhook
}
Use the rawBody field for signature verification, as it contains the unmodified request body string.

Validate Request Source

Check headers to ensure requests come from expected sources:
body: async ({ headers, body }) => {
  const userAgent = headers["user-agent"];
  
  // Verify source (e.g., "Stripe/1.0")
  if (!userAgent?.startsWith("Stripe/")) {
    throw new Error("Unauthorized webhook source");
  }
  
  // Process webhook
}

Handle Sensitive Data Safely

body: async ({ body }) => {
  // Don't log sensitive information
  const sanitized = {
    event: body.event,
    timestamp: body.timestamp,
    // Omit sensitive fields like tokens, emails, etc.
  };
  
  console.log("Webhook received:", sanitized);
}

Advanced Patterns

Webhook to Workflow

Trigger workflows from incoming webhooks:
import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";
import orderProcessingWorkflow from "../workflows/order-processing";

export default createExecutor({
  name: "order-webhook",
  trigger: incomingWebhookTrigger<{
    body: { orderId: string; customerId: string };
    headers: Record<string, string>;
  }>(),
  operation: {
    kind: "workflow",
    workflow: orderProcessingWorkflow,
    args: ({ body }) => ({
      orderId: body.orderId,
      customerId: body.customerId,
    }),
  },
});

Webhook to GraphQL

Execute GraphQL mutations from webhook events:
export default createExecutor({
  name: "webhook-to-graphql",
  trigger: incomingWebhookTrigger<{
    body: { userId: string; status: string };
    headers: Record<string, string>;
  }>(),
  operation: {
    kind: "graphql",
    query: `
      mutation UpdateUserStatus($userId: ID!, $status: String!) {
        updateUser(id: $userId, input: { status: $status }) {
          id
          status
        }
      }
    `,
    variables: ({ body }) => ({
      userId: body.userId,
      status: body.status,
    }),
  },
});

Webhook Forwarding

Forward webhooks to other services:
export default createExecutor({
  name: "webhook-forwarder",
  trigger: incomingWebhookTrigger<{
    body: Record<string, unknown>;
    headers: Record<string, string>;
  }>(),
  operation: {
    kind: "webhook",
    url: "https://api.internal.com/webhooks/process",
    headers: {
      "Content-Type": "application/json",
      "X-Forwarded-From": "tailor-platform",
    },
    requestBody: ({ body, headers }) => ({
      originalBody: body,
      receivedAt: new Date().toISOString(),
      sourceHeaders: headers,
    }),
  },
});

Best Practices

Type Your Webhooks

Define clear interfaces for webhook payloads:
interface MyServiceWebhook {
  event: "user.created" | "user.updated" | "user.deleted";
  timestamp: string;
  data: {
    userId: string;
    email: string;
  };
}

incomingWebhookTrigger<{
  body: MyServiceWebhook;
  headers: { "x-api-key": string };
}>()

Implement Idempotency

Prevent duplicate processing:
body: async ({ body }) => {
  const db = getDB("tailordb");
  const webhookId = body.id;
  
  // Check if already processed
  const existing = await db
    .selectFrom("ProcessedWebhooks")
    .where("webhookId", "=", webhookId)
    .executeTakeFirst();
  
  if (existing) {
    console.log("Webhook already processed");
    return;
  }
  
  // Process webhook
  // ...
  
  // Mark as processed
  await db
    .insertInto("ProcessedWebhooks")
    .values({ webhookId, processedAt: new Date() })
    .execute();
}

Handle Errors Gracefully

body: async ({ body }) => {
  try {
    // Process webhook
  } catch (error) {
    console.error("Webhook processing failed:", error);
    // Log to monitoring system
    // Consider retry logic
    throw error; // Re-throw to signal failure
  }
}

Respond Quickly

Most webhook providers expect fast responses (< 5 seconds):
// For long-running tasks, trigger a workflow
operation: {
  kind: "workflow",
  workflow: longRunningTask,
  args: ({ body }) => ({ data: body }),
}

// Or use jobFunction
operation: {
  kind: "jobFunction",
  body: async ({ body }) => {
    // Long-running processing
  },
}
Webhook endpoints are publicly accessible by default. Always implement authentication and validation to prevent abuse.

Webhook URL

After deploying your executor, the webhook URL will be available in your Tailor Platform dashboard. The URL format is typically:
https://{workspace}.tailor.tech/webhooks/{executor-name}
Provide this URL to the external service you’re integrating with.

Build docs developers (and LLMs) love