Webhook Triggers

Execute workflows automatically when external systems send HTTP requests to a unique webhook URL. Perfect for integrating with third-party services, processing real-time events, and building event-driven automation.

Overview

Webhook triggers create a secure HTTP endpoint that external systems can call to start your workflow. When the webhook receives a request, it automatically creates a new workflow run with the request data as input.

Key Features:

  • Unique, secure webhook URL for each trigger

  • Multiple authentication methods (None, Basic, Bearer)

  • Configurable HTTP methods (GET, POST, PUT, DELETE)

  • Custom response codes

  • Immediate response with workflow run tracking

  • Full event history and logging

How Webhook Triggers Work

Execution Flow

External System → HTTP Request → Webhook URL → Trigger Validation →
→ Workflow Run Created → Immediate Response → Workflow Executes
  1. External system sends HTTP request to webhook URL

  2. Platform validates authentication and HTTP method

  3. Trigger creates new workflow run with request data

  4. Immediate response returned with run ID and status

  5. Workflow executes asynchronously in background

  6. Event logged with full request/response details

Response Behavior

Webhooks use immediate response mode:

  • HTTP response returned within milliseconds

  • Response includes workflow run ID

  • Workflow continues executing asynchronously

  • External system doesn't wait for workflow completion

Example webhook response:

{
  "workflow_run_id": "01HXXX123456789ABCDEF",
  "workflow_run_status": "QUEUED"
}

Understanding Webhook Input Data

CRITICAL CONCEPT: The webhook request body is passed directly as your workflow's input. Understanding how to configure your input schema and access webhook data is essential for successful integration.

How Webhook Data Becomes Workflow Input

When a webhook receives a request, the entire request body is automatically passed as the workflow's input data.

The flow:

  1. External system sends JSON data to webhook URL

  2. Platform receives the request body

  3. Request body becomes workflow input

  4. Fields defined in your input schema are accessible using {{field_name}}

Configuring Workflow Input Schema for Webhooks

Your workflow's input schema defines what data the workflow expects. For webhook triggers, you must define each field you want to access in your workflow.

Supported field types:

  • string - Text data

  • number - Numeric values

  • boolean - True/false values

  • array - Lists of items

  • object - Nested objects (accepts any structure)

Important: Object Type Limitation

You must define each top-level field, but object fields accept any internal structure:

  • ✅ You must list each field in properties

  • ✅ You can specify a field is type object

  • ❌ You cannot define the internal structure/properties of an object field

  • ✅ All nested data within object fields is accessible via dot notation

Input Schema Examples

Example 1: Simple Fields

Webhook payload:

{
  "customer_email": "[email protected]",
  "order_id": "ORD-12345",
  "amount": 99.99,
  "premium": true
}

Workflow input schema (you must define all fields):

{
  "type": "object",
  "properties": {
    "customer_email": {
      "type": "string",
      "title": "Customer Email"
    },
    "order_id": {
      "type": "string",
      "title": "Order ID"
    },
    "amount": {
      "type": "number",
      "title": "Amount"
    },
    "premium": {
      "type": "boolean",
      "title": "Premium Customer"
    }
  }
}

Access in workflow:

{{customer_email}}  → "[email protected]"
{{order_id}}        → "ORD-12345"
{{amount}}          → 99.99
{{premium}}         → true

Example 2: Object Fields (No Internal Schema Needed)

Webhook payload:

{
  "order_id": "ORD-789",
  "customer": {
    "id": "cust_123",
    "name": "Jane Smith",
    "email": "[email protected]",
    "address": {
      "city": "New York",
      "zip": "10001"
    }
  }
}

Workflow input schema:

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "title": "Order ID"
    },
    "customer": {
      "type": "object",
      "title": "Customer Data"
    }
  }
}

Key point:

  • We must define customer as a field in properties

  • We specify customer is type object

  • We don't need to define its internal properties (id, name, email, address)

  • The system automatically accepts any structure inside the customer object

Access nested data with dot notation:

{{order_id}}                → "ORD-789"
{{customer.id}}             → "cust_123"
{{customer.name}}           → "Jane Smith"
{{customer.email}}          → "[email protected]"
{{customer.address.city}}   → "New York"
{{customer.address.zip}}    → "10001"

Even though we didn't define id, name, email, or address in the schema, we can still access them because customer is type object!

Example 3: Array Fields

Webhook payload:

{
  "order_id": "ORD-456",
  "items": [
    {"sku": "PROD-001", "quantity": 2, "price": 29.99},
    {"sku": "PROD-002", "quantity": 1, "price": 49.99}
  ],
  "tags": ["urgent", "vip", "international"]
}

Workflow input schema:

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "title": "Order ID"
    },
    "items": {
      "type": "array",
      "title": "Order Items"
    },
    "tags": {
      "type": "array",
      "title": "Tags"
    }
  }
}

Access in workflow:

{{order_id}}          → "ORD-456"
{{items}}             → Full array of objects
{{items[0].sku}}      → "PROD-001"
{{items[0].quantity}} → 2
{{items[1].price}}    → 49.99
{{tags}}              → ["urgent", "vip", "international"]
{{tags[0]}}           → "urgent"

Strategy: Define Top-Level Fields You Need

Best Practice: Define each top-level field from the webhook payload that your workflow will access. Use type object for complex nested structures.

Example - Stripe webhook:

Webhook sends this payload:

{
  "id": "evt_123",
  "type": "charge.succeeded",
  "data": {
    "object": {
      "id": "ch_456",
      "amount": 5000,
      "currency": "usd",
      "customer": "cus_789",
      "receipt_email": "[email protected]",
      "metadata": {
        "order_id": "ORD-12345",
        "product": "Premium Plan"
      }
    }
  },
  "created": 1234567890,
  "livemode": false
}

Your workflow input schema (define what you'll use):

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "title": "Event ID"
    },
    "type": {
      "type": "string",
      "title": "Event Type"
    },
    "data": {
      "type": "object",
      "title": "Event Data"
    }
  }
}

Even though we only defined id, type, and data (as object), you can still access all nested data:

{{type}}                                 → "charge.succeeded"
{{data.object.amount}}                   → 5000
{{data.object.receipt_email}}            → "[email protected]"
{{data.object.metadata.order_id}}        → "ORD-12345"
{{data.object.metadata.product}}         → "Premium Plan"

The data field is defined as type object, so all its nested content is automatically accessible!

Accessing Webhook Data in Workflow Nodes

Once your input schema is configured, reference webhook data using parameter substitution syntax: {{field_name}}

Simple Fields

{{customer_email}}
{{order_id}}
{{amount}}
{{premium}}

Nested Fields (Dot Notation)

{{customer.name}}
{{customer.email}}
{{customer.address.city}}
{{data.object.amount}}
{{metadata.order_id}}

Array Elements

{{items[0].sku}}
{{items[1].price}}
{{tags[0]}}

Use in Node Configurations

Email node example:

{
  "to": "{{customer.email}}",
  "subject": "Order {{order_id}} Confirmation",
  "body": "Hello {{customer.name}}, your order total is ${{amount}}"
}

API call node example:

{
  "url": "https://api.example.com/orders",
  "method": "POST",
  "body": {
    "order_id": "{{order_id}}",
    "customer_email": "{{customer.email}}",
    "total": "{{amount}}"
  }
}

Processing Complex Data with JavaScript

For complex transformations or accessing deeply nested data, use JavaScript nodes:

JavaScript node example:

// Access the full customer object
const customer = {{customer}};

// Extract specific fields
const fullName = customer.name;
const email = customer.email;
const city = customer.address.city;

// Process arrays
const items = {{items}};
const total = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const productNames = items.map(item => item.sku).join(", ");

// Return processed data
return {
  fullName,
  email,
  city,
  total,
  productNames,
  itemCount: items.length
};

Real-World Integration Examples

Example 1: Stripe Payment Webhook

Stripe payload structure:

{
  "id": "evt_1ABC123",
  "type": "charge.succeeded",
  "data": {
    "object": {
      "id": "ch_3XYZ789",
      "amount": 5000,
      "currency": "usd",
      "receipt_email": "[email protected]",
      "metadata": {
        "order_id": "ORD-12345"
      }
    }
  }
}

Your workflow input schema:

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "title": "Event ID"
    },
    "type": {
      "type": "string",
      "title": "Event Type"
    },
    "data": {
      "type": "object",
      "title": "Event Data"
    }
  }
}

Workflow logic:

  1. Node 1 - Validate event type:

    const eventType = {{type}};
    if (eventType !== "charge.succeeded") {
      throw new Error("Unsupported event type: " + eventType);
    }
  2. Node 2 - Send confirmation email:

    To: {{data.object.receipt_email}}
    Subject: Payment Received - Order {{data.object.metadata.order_id}}
    Body: Thank you! We received your payment of ${{data.object.amount}}.
  3. Node 3 - Update database with order status:

    Order ID: {{data.object.metadata.order_id}}
    Amount: {{data.object.amount}}
    Currency: {{data.object.currency}}
    Status: "paid"

Example 2: GitHub Pull Request Webhook

GitHub payload:

{
  "action": "opened",
  "number": 42,
  "pull_request": {
    "title": "Add new feature",
    "user": {
      "login": "developer123"
    },
    "head": {
      "ref": "feature-branch",
      "sha": "abc123"
    },
    "base": {
      "ref": "main"
    }
  },
  "repository": {
    "full_name": "org/my-project"
  }
}

Your workflow input schema:

{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "title": "Action"
    },
    "number": {
      "type": "number",
      "title": "PR Number"
    },
    "pull_request": {
      "type": "object",
      "title": "Pull Request Data"
    },
    "repository": {
      "type": "object",
      "title": "Repository"
    }
  }
}

Access data in workflow:

{{action}}                       → "opened"
{{number}}                       → 42
{{pull_request.title}}           → "Add new feature"
{{pull_request.user.login}}      → "developer123"
{{pull_request.head.ref}}        → "feature-branch"
{{pull_request.head.sha}}        → "abc123"
{{repository.full_name}}         → "org/my-project"

Example workflow:

  1. Node 1 - Post PR comment:

    Message: "Thanks @{{pull_request.user.login}} for PR #{{number}}!
             Automated checks are running..."
  2. Node 2 - Notify team on Slack:

    Channel: #pull-requests
    Message: "New PR in {{repository.full_name}}: {{pull_request.title}}
             Branch: {{pull_request.head.ref}} → {{pull_request.base.ref}}"

Example 3: Form Submission

Form payload:

{
  "first_name": "John",
  "last_name": "Doe",
  "email": "[email protected]",
  "company": "Acme Corp",
  "message": "I'm interested in your services"
}

Your workflow input schema:

{
  "type": "object",
  "properties": {
    "first_name": {
      "type": "string",
      "title": "First Name"
    },
    "last_name": {
      "type": "string",
      "title": "Last Name"
    },
    "email": {
      "type": "string",
      "title": "Email"
    },
    "company": {
      "type": "string",
      "title": "Company"
    },
    "message": {
      "type": "string",
      "title": "Message"
    }
  }
}

Use in workflow:

Full Name: {{first_name}} {{last_name}}
Email: {{email}}
Company: {{company}}
Inquiry: {{message}}

Example workflow:

  1. Node 1 - Send auto-reply:

    To: {{email}}
    Subject: Thanks for contacting us, {{first_name}}!
    Body: We received your message and will get back to you soon.
  2. Node 2 - Create CRM lead:

    Name: {{first_name}} {{last_name}}
    Email: {{email}}
    Company: {{company}}
    Source: "Website Contact Form"
  3. Node 3 - Notify sales team:

    Channel: #new-leads
    Message: "New inquiry from {{first_name}} {{last_name}} at {{company}}"

Common Mistakes and Solutions

Mistake 1: Trying to Define Object Properties

Problem (❌ This doesn't work):

{
  "type": "object",
  "properties": {
    "customer": {
      "type": "object",
      "properties": {  // ❌ NOT supported!
        "name": {"type": "string"},
        "email": {"type": "string"}
      }
    }
  }
}

Solution (✅ Correct):

{
  "type": "object",
  "properties": {
    "customer": {
      "type": "object",  // ✅ Just define as object
      "title": "Customer Data"
    }
  }
}

You can still access {{customer.name}} and {{customer.email}} - you just don't define them in the schema!

Mistake 2: Not Defining a Field at All

Problem:

// Webhook sends: {"order_id": "123", "customer": {...}}

// Input schema (missing customer field):
{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string"
    }
    // ❌ customer field not defined
  }
}

// In workflow:
{{customer.name}}  // ❌ Won't work - customer not defined

Solution (✅ Define all top-level fields):

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string"
    },
    "customer": {
      "type": "object"  // ✅ Now customer.name is accessible
    }
  }
}

Mistake 3: Field Name Mismatch

Problem:

Webhook sends: {"customer_email": "[email protected]"}
Schema defines: {"user_email": {"type": "string"}}
Workflow uses: {{user_email}}  // ❌ Returns undefined

Solution: Match field names exactly:

{
  "type": "object",
  "properties": {
    "customer_email": {  // ✅ Exact match
      "type": "string",
      "title": "Customer Email"
    }
  }
}

Mistake 4: Case Sensitivity

Problem:

Webhook sends: {"CustomerEmail": "[email protected]"}
Schema defines: {"customeremail": {...}}
Workflow uses: {{customeremail}}  // ❌ Case doesn't match

Solution: Field names are case-sensitive:

{
  "type": "object",
  "properties": {
    "CustomerEmail": {  // ✅ Match exact case
      "type": "string"
    }
  }
}

Use in workflow:

{{CustomerEmail}}  ✅ Correct
{{customeremail}}  ❌ Wrong case

Mistake 5: Wrong Data Type

Problem:

Webhook sends: {"amount": "99.99"}  (string)
Schema defines: {"amount": {"type": "number"}}
// May cause type mismatch issues

Solution: Match the actual type the webhook sends:

{
  "type": "object",
  "properties": {
    "amount": {
      "type": "string"  // ✅ Match webhook's type
    }
  }
}

Then convert in workflow if needed:

const amountNumber = parseFloat({{amount}});

Testing Your Webhook Integration

Step 1: Identify Webhook Payload Structure

Before creating your input schema, understand what data the webhook will send:

  1. Check third-party documentation (Stripe, GitHub, etc.)

  2. Capture a test webhook using tools like:

    • webhook.site

    • RequestBin

    • ngrok

  3. Note all top-level field names

  4. Identify which fields are objects, arrays, or primitives

Step 2: Create Input Schema

Based on the payload structure, create your input schema:

{
  "type": "object",
  "properties": {
    "field1": {"type": "string"},
    "field2": {"type": "number"},
    "field3": {"type": "object"},  // For nested data
    "field4": {"type": "array"}    // For lists
  }
}

Step 3: Send Test Request

curl -X POST https://api.agenticflow.com/webhook/{your-path-id} \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token" \
  -d '{
    "field1": "test_value",
    "field2": 123,
    "field3": {
      "nested": "data"
    },
    "field4": ["item1", "item2"]
  }'

Step 4: Check Trigger Event Log

  1. Workflow → Triggers → Your webhook → Events tab

  2. Find your test request

  3. Inspect request_body

  4. Verify all fields are present

Step 5: Verify Workflow Execution

  1. Click workflow_run_id from event

  2. Check workflow run "Input Data"

  3. Confirm fields are accessible

  4. Review node execution logs

Step 6: Add Debug Node

Add a JavaScript node to test field access:

console.log("field1:", {{field1}});
console.log("field2:", {{field2}});
console.log("field3.nested:", {{field3.nested}});
console.log("field4[0]:", {{field4[0]}});

return {
  field1: {{field1}},
  field2: {{field2}},
  nested_value: {{field3.nested}},
  first_item: {{field4[0]}}
};

Creating a Webhook Trigger

Step 1: Navigate to Triggers

  1. Open your workflow

  2. Click "Triggers" tab

  3. Click "Create Trigger"

  4. Select "Webhook" type

Step 2: Configure Basic Settings

Name (required):

Example: "Stripe Payment Webhook"

Description (optional):

Example: "Processes payment.succeeded events from Stripe"

Step 3: Configure HTTP Settings

HTTP Method:

  • POST (recommended) - Data in request body

  • GET - Data in query parameters

  • PUT - Update operations

  • DELETE - Delete operations

Response Code:

  • 200 - Standard success (default)

  • 201 - Resource created

  • 202 - Accepted for processing

  • 204 - Success, no content

Response Mode:

  • Immediate - Returns response immediately with workflow run ID

Step 4: Configure Authentication

Bearer Token (Recommended for production):

{
  "auth_type": "BEARER",
  "token": "your_secret_token"
}

Callers must include:

Authorization: Bearer your_secret_token

Basic Authentication:

{
  "auth_type": "BASIC",
  "username": "user",
  "password": "pass"
}

Callers must include:

Authorization: Basic <base64(username:password)>

No Authentication (Testing only):

{
  "auth_type": "NONE"
}

⚠️ Not recommended for production.

Step 5: Save and Get Webhook URL

  1. Click "Create Trigger"

  2. Copy generated webhook URL:

    https://api.agenticflow.com/webhook/{unique-path-id}
  3. Configure URL in external system

  4. Webhook is immediately active

Using Your Webhook

Making Requests

Basic POST request:

curl -X POST https://api.agenticflow.com/webhook/{path-id} \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token" \
  -d '{
    "customer_email": "[email protected]",
    "order_id": "ORD-123",
    "amount": 99.99
  }'

Response:

{
  "workflow_run_id": "01HXXX123456789ABCDEF",
  "workflow_run_status": "QUEUED"
}

Security Best Practices

  1. Always use authentication in production (Bearer token recommended)

  2. Use strong, random tokens (32+ characters minimum)

  3. Rotate credentials regularly (every 90 days)

  4. Monitor webhook events for suspicious activity

  5. Validate input data in workflow nodes

  6. Use HTTPS (enforced automatically)

  7. Never commit tokens to version control

Managing Webhooks

Enable/Disable:

  • Active: Accepts requests, creates workflow runs

  • Inactive: Returns 404, doesn't execute

Update:

  • Change name, description

  • Update authentication credentials

  • Modify HTTP method or response code

  • Toggle active status

Delete:

  • Permanent action - cannot be undone

  • Webhook URL becomes invalid immediately

  • External systems will receive 404 errors

Webhook Events

Every webhook request is logged with complete details:

Event information:

  • Timestamp of request

  • HTTP method, URL, headers

  • Full request body

  • Response status code and body

  • Execution status (success/failed)

  • Workflow run ID (if created)

  • Error messages (if any)

View events: Workflow → Triggers → Select webhook → Events tab

Use event logs for:

  • Debugging integration issues

  • Monitoring webhook activity

  • Auditing workflow executions

  • Investigating failed requests

Troubleshooting

Field Returns Undefined

Check:

  1. Is the field defined in input schema properties?

  2. Does the field name match exactly (case-sensitive)?

  3. Is the webhook actually sending that field?

  4. Check trigger event logs to see actual payload received

Nested Data Not Accessible

Problem: Can't access {{customer.email}}

Check:

  1. Is customer defined as type object in schema?

  2. Are you using correct dot notation?

  3. Did webhook actually send nested data?

Type Mismatch Errors

Solution: Ensure schema type matches what webhook sends:

  • If webhook sends "123" (string), use type: "string"

  • If webhook sends 123 (number), use type: "number"

  • Convert types in JavaScript node if needed

API Reference

Create Webhook Trigger

POST /workflows/{workflow_id}/triggers
Content-Type: application/json
Authorization: Bearer your_api_key

Request:

{
  "name": "My Webhook",
  "description": "Webhook description",
  "trigger_type": "webhook",
  "trigger_config": {
    "auth_config": {
      "auth_type": "BEARER",
      "token": "secret_token"
    },
    "method": "POST",
    "response_code": 200,
    "response_mode": "immediate"
  }
}

Response:

{
  "id": "01HXXX123456789ABCDEF",
  "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "My Webhook",
  "trigger_config": {
    "path": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "method": "POST",
    "response_code": 200
  },
  "is_active": true,
  "created_at": "2024-01-15T10:30:00Z"
}


Ready to integrate external systems? Create your first webhook trigger and configure your workflow inputs to match your webhook payload structure.

Last updated

Was this helpful?