> For the complete documentation index, see [llms.txt](https://docs.agenticflow.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.agenticflow.ai/workflows/triggers/webhook-triggers.md).

# 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**:

```json
{
  "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**:

```json
{
  "customer_email": "user@example.com",
  "order_id": "ORD-12345",
  "amount": 99.99,
  "premium": true
}
```

**Workflow input schema** (you must define all fields):

```json
{
  "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}}  → "user@example.com"
{{order_id}}        → "ORD-12345"
{{amount}}          → 99.99
{{premium}}         → true
```

#### Example 2: Object Fields (No Internal Schema Needed)

**Webhook payload**:

```json
{
  "order_id": "ORD-789",
  "customer": {
    "id": "cust_123",
    "name": "Jane Smith",
    "email": "jane@example.com",
    "address": {
      "city": "New York",
      "zip": "10001"
    }
  }
}
```

**Workflow input schema**:

```json
{
  "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}}          → "jane@example.com"
{{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**:

```json
{
  "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**:

```json
{
  "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:

```json
{
  "id": "evt_123",
  "type": "charge.succeeded",
  "data": {
    "object": {
      "id": "ch_456",
      "amount": 5000,
      "currency": "usd",
      "customer": "cus_789",
      "receipt_email": "customer@example.com",
      "metadata": {
        "order_id": "ORD-12345",
        "product": "Premium Plan"
      }
    }
  },
  "created": 1234567890,
  "livemode": false
}
```

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

```json
{
  "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}}            → "customer@example.com"
{{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**:

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

**API call node example**:

```json
{
  "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**:

```javascript
// 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**:

```json
{
  "id": "evt_1ABC123",
  "type": "charge.succeeded",
  "data": {
    "object": {
      "id": "ch_3XYZ789",
      "amount": 5000,
      "currency": "usd",
      "receipt_email": "customer@example.com",
      "metadata": {
        "order_id": "ORD-12345"
      }
    }
  }
}
```

**Your workflow input schema**:

```json
{
  "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:

   ```javascript
   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**:

```json
{
  "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**:

```json
{
  "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**:

```json
{
  "first_name": "John",
  "last_name": "Doe",
  "email": "john@example.com",
  "company": "Acme Corp",
  "message": "I'm interested in your services"
}
```

**Your workflow input schema**:

```json
{
  "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):

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

**Solution** (✅ Correct):

```json
{
  "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**:

```json
// 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):

```json
{
  "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": "user@example.com"}
Schema defines: {"user_email": {"type": "string"}}
Workflow uses: {{user_email}}  // ❌ Returns undefined
```

**Solution**: Match field names exactly:

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

### Mistake 4: Case Sensitivity

**Problem**:

```
Webhook sends: {"CustomerEmail": "user@example.com"}
Schema defines: {"customeremail": {...}}
Workflow uses: {{customeremail}}  // ❌ Case doesn't match
```

**Solution**: Field names are case-sensitive:

```json
{
  "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:

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

Then convert in workflow if needed:

```javascript
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:

```json
{
  "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

```bash
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:

```javascript
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):

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

Callers must include:

```
Authorization: Bearer your_secret_token
```

**Basic Authentication**:

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

Callers must include:

```
Authorization: Basic <base64(username:password)>
```

**No Authentication** (Testing only):

```json
{
  "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**:

```bash
curl -X POST https://api.agenticflow.com/webhook/{path-id} \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token" \
  -d '{
    "customer_email": "user@example.com",
    "order_id": "ORD-123",
    "amount": 99.99
  }'
```

**Response**:

```json
{
  "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

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

**Request**:

```json
{
  "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**:

```json
{
  "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"
}
```

***

## Related Documentation

* [Triggers Overview](/workflows/triggers.md) - All trigger types
* [Schedule Triggers](/workflows/triggers/schedule-triggers.md) - Time-based triggers
* [Workflow Inputs](/workflows/workflow-inputs/text-input.md) - Input configuration guide
* [Data Flow & Types](/workflows/data-flow-and-types.md) - Working with data in workflows

***

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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.agenticflow.ai/workflows/triggers/webhook-triggers.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
