---
title: "What is the Webhook Approval Step?"
icon: "check-to-slot"
---

## What is the Webhook Approval Step?

The **Webhook Approval Step** enables you to pause your agent's execution and wait for approval from an external system before continuing. This is perfect for workflows that require human review, compliance checks, or integration with your existing approval processes.

### Common Use Cases

- **Financial Approvals**: Pause before processing transactions above a certain threshold
- **Compliance Reviews**: Get legal team approval before sending sensitive communications
- **Quality Gates**: Integrate with your CI/CD pipeline for deployment approvals
- **Customer Onboarding**: Route high-value leads through your CRM approval workflow
- **IT Operations**: Require manager approval before executing infrastructure changes

---

## How It Works

```
Agent Execution → Reaches Approval Step → Sends Webhook → External System → Approval/Denial → Agent Continues
```

1. **Agent pauses**: When the agent reaches this step, execution pauses automatically
2. **Webhook sent**: Airia sends a POST request to your configured URL with execution details
3. **You decide**: Your system receives the request and applies your business logic
4. **Send response**: Your system calls back to Airia with "Approved" or "Denied"
5. **Agent continues**: Based on your decision, the agent follows the appropriate path

---

## Configuration Guide

### 1. Webhook URL

**What it is**: The endpoint where Airia will send approval requests.

**Requirements**:
- Must be a publicly accessible HTTPS URL
- Must accept POST requests
- Must return a 2xx status code to confirm receipt

**Examples**:
```
✅ https://api.yourcompany.com/airia/approve
✅ https://approvals.yourcompany.com/webhook
✅ https://your-app.herokuapp.com/webhook/airia

❌ http://localhost:3000/approve (not public)
❌ http://api.yourcompany.com/approve (not HTTPS)
```

**Tips**:
- Use a dedicated endpoint for Airia approvals
- Ensure the URL is stable and won't change
- Test accessibility from external networks
- For development, use tools like [ngrok](https://ngrok.com) to expose local servers

---

### 2. Authentication

**What it is**: Security method to verify requests are coming from Airia.

**Options**:

#### None
No authentication required. Only use for internal testing.

#### API Key
Airia will include your API key in the `X-API-Key` header.

**When to use**: Simple authentication for internal systems.

**Configuration**:
```json
{
  "authenticationType": "ApiKey",
  "authenticationConfig": {
    "apiKey": "your-secret-key-here"
  }
}
```

**Your endpoint should verify**:
```python
if request.headers.get('X-API-Key') != 'your-secret-key-here':
    return 401
```

#### Bearer Token
Airia will include your token in the `Authorization: Bearer` header.

**When to use**: Standard OAuth-style authentication.

**Configuration**:
```json
{
  "authenticationType": "BearerToken",
  "authenticationConfig": {
    "token": "your-bearer-token"
  }
}
```

#### Basic Auth
Airia will include username and password in the `Authorization: Basic` header.

**When to use**: Legacy systems requiring basic authentication.

**Configuration**:
```json
{
  "authenticationType": "BasicAuth",
  "authenticationConfig": {
    "username": "your-username",
    "password": "your-password"
  }
}
```

#### Custom Header
Airia will include a custom header with your specified name and value.

**When to use**: Your system uses non-standard authentication headers.

**Configuration**:
```json
{
  "authenticationType": "CustomHeader",
  "authenticationConfig": {
    "headerName": "X-Custom-Auth",
    "headerValue": "your-custom-value"
  }
}
```

**🔒 Security Tip**: Always use authentication in production environments.

---

### 3. Advanced Settings

#### Timeout (seconds)
**Default**: 300 (5 minutes)

**What it is**: Maximum time Airia will wait for your endpoint to respond.

**Recommendations**:
- **Fast APIs**: 30-60 seconds
- **Standard processing**: 300 seconds (default)
- **Complex workflows**: 600 seconds (10 minutes)

**Important**: This is just for the initial webhook delivery, not the approval decision. Your system can take as long as needed to send the approval/denial callback.

#### Max Retry Attempts
**Default**: 3

**What it is**: How many times Airia will retry if your endpoint is unavailable.

**How it works**:
- Retry 1: After 2 seconds
- Retry 2: After 4 seconds
- Retry 3: After 8 seconds
- (Exponential backoff)

**Recommendations**:
- **Reliable endpoints**: 1-2 retries
- **Less reliable**: 3-5 retries
- **Development/testing**: 1 retry

**Cost consideration**: Each retry counts as a separate request, so set appropriately based on your endpoint reliability.

---

### 4. Message

**What it is**: Optional custom message included in the webhook payload to provide context.

**Examples**:
```
"Please review this high-value customer onboarding request"
"Approval required: Transaction amount exceeds $10,000"
"Compliance review needed before sending communication"
"Manager approval required for infrastructure change"
```

**Tips**:
- Keep it concise (under 500 characters recommended)
- Include context that helps the approver make a decision
- Use dynamic variables if your system supports them
- Can be left blank if your webhook URL is specific enough

---

## Setting Up Your Webhook Endpoint

### What You'll Receive

When your agent reaches the Webhook Approval step, Airia will POST this to your endpoint:

```json
{
  "agentExecutionWebhookApprovalId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "executionId": "7b8c9d10-1234-5678-9abc-def012345678",
  "agentId": "a1b2c3d4-5678-90ef-1234-567890abcdef",
  "agentName": "Customer Onboarding Agent",
  "projectId": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
  "message": "Please review this high-value customer",
  "stepInput": "Customer: Acme Corp, Value: $50,000",
  "previousResults": {
    "step-1": { "Value": "Credit check passed" },
    "step-2": { "Value": "Documents verified" }
  },
  "callbackId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "callbackToken": "abc123xyz789..."
}
```

**Key Fields**:
- `callbackId`: Unique ID for this approval (you'll need this to respond)
- `callbackToken`: Security token (REQUIRED to respond - store this securely!)
- `agentName`: Name of the agent requesting approval
- `message`: Your custom message from configuration
- `stepInput`: Data passed into this step
- `previousResults`: Results from all previous steps in the workflow

---

### How to Respond

Your system must call this endpoint to approve or deny:

**Endpoint**: `POST https://api.airia.com/v1/AgentExecutionWebhookApproval/callback`

**Request Body**:
```json
{
  "callbackId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "callbackToken": "abc123xyz789...",
  "status": "Approved",
  "comment": "Approved by John Doe - All checks passed"
}
```

**Required Fields**:
- `callbackId`: From the webhook you received
- `callbackToken`: From the webhook you received (MUST match exactly)
- `status`: Must be exactly `"Approved"` or `"Denied"` (case-sensitive)

**Optional Fields**:
- `comment`: Explanation for the decision (visible in execution logs)

---

## Quick Start Examples

### Example 1: Simple Python Webhook

```python
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

# Store these securely in production
PENDING_APPROVALS = {}

@app.route('/webhook/approve', methods=['POST'])
def receive_approval_request():
    """Receive approval request from Airia"""
    data = request.json

    # Validate authentication (if configured)
    if request.headers.get('X-API-Key') != 'your-secret-key':
        return jsonify({'error': 'Unauthorized'}), 401

    # Store the approval for processing
    approval_id = data['callbackId']
    PENDING_APPROVALS[approval_id] = {
        'token': data['callbackToken'],
        'agent': data['agentName'],
        'message': data['message'],
        'input': data['stepInput']
    }

    print(f"Received approval request: {data['message']}")

    # Return 200 immediately - process asynchronously
    return jsonify({'received': True}), 200

def approve_request(approval_id, approved, comment):
    """Send approval decision back to Airia"""
    approval = PENDING_APPROVALS.get(approval_id)
    if not approval:
        print(f"Approval {approval_id} not found")
        return

    callback_url = "https://api.airia.com/v1/AgentExecutionWebhookApproval/callback"

    response = requests.post(callback_url, json={
        'callbackId': approval_id,
        'callbackToken': approval['token'],
        'status': 'Approved' if approved else 'Denied',
        'comment': comment
    })

    if response.status_code == 200:
        print(f"Successfully sent approval decision: {approved}")
        del PENDING_APPROVALS[approval_id]
    else:
        print(f"Failed to send approval: {response.text}")

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
```
