> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP Webhook Channel

> Programmatic access to IronClaw via HTTP POST requests

The HTTP webhook channel provides a simple REST API for sending messages to IronClaw and receiving responses.

## Features

* **JSON API** - Simple POST requests with JSON payloads
* **Webhook secret** - HMAC-based authentication
* **Synchronous responses** - Optional wait for agent reply
* **Thread support** - Conversation continuity via `thread_id`
* **Rate limiting** - 60 requests/minute
* **Fixed user ID** - Single user per channel instance

## Configuration

Set via environment variables or `.env` file:

```bash theme={null}
# HTTP server host and port
HTTP_HOST=0.0.0.0
HTTP_PORT=8080

# Required: Webhook secret for authentication
HTTP_WEBHOOK_SECRET=your-webhook-secret
```

### Configuration Options

| Variable | Type | Default | Description |
| - | - | - | - |
| `HTTP_HOST` | `string` | `"0.0.0.0"` | Bind address |
| `HTTP_PORT` | `integer` | `8080` | HTTP port |
| `HTTP_WEBHOOK_SECRET` | `string` | *required* | Secret for request authentication |

## Endpoints

### Health Check

```bash theme={null}
GET /health
```

**Response:**

```json theme={null}
{
  "status": "healthy",
  "channel": "http"
}
```

### Send Message (Async)

```bash theme={null}
POST /webhook
Content-Type: application/json

{
  "content": "Hello, agent!",
  "secret": "your-webhook-secret"
}
```

**Response (202 Accepted):**

```json theme={null}
{
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "accepted"
}
```

The agent processes the message asynchronously. Listen for responses via SSE or WebSocket (Web Gateway channel).

### Send Message (Sync)

```bash theme={null}
POST /webhook
Content-Type: application/json

{
  "content": "What is 2+2?",
  "secret": "your-webhook-secret",
  "wait_for_response": true
}
```

**Response (200 OK):**

```json theme={null}
{
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "accepted",
  "response": "2 + 2 equals 4."
}
```

The request blocks (up to 60 seconds) until the agent responds.

### Thread Support

```bash theme={null}
POST /webhook
Content-Type: application/json

{
  "content": "Continue the conversation",
  "thread_id": "thread-123",
  "secret": "your-webhook-secret"
}
```

Messages with the same `thread_id` are tracked as a single conversation. The agent loads conversation history when a matching thread is found.

## Request Schema

```typescript theme={null}
interface WebhookRequest {
  // Message content (required)
  content: string;

  // Webhook secret (required)
  secret: string;

  // Optional thread ID for conversation tracking
  thread_id?: string;

  // Wait for synchronous response (default: false)
  wait_for_response?: boolean;

  // User ID (ignored, fixed by server config)
  user_id?: string;
}
```

## Response Schema

```typescript theme={null}
interface WebhookResponse {
  // Assigned message ID
  message_id: string;

  // Status: "accepted" or "error"
  status: string;

  // Response content (only if wait_for_response was true)
  response?: string;
}
```

## Error Responses

### 401 Unauthorized - Invalid Secret

```json theme={null}
{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Invalid webhook secret"
}
```

### 401 Unauthorized - Missing Secret

```json theme={null}
{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Webhook secret required"
}
```

### 413 Payload Too Large

```json theme={null}
{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Content too large"
}
```

**Limits:**

* Max body size: 64 KB
* Max content length: 32 KB

### 429 Too Many Requests

```json theme={null}
{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Rate limit exceeded"
}
```

**Rate limits:**

* 60 requests per minute
* 100 pending wait-for-response requests

### 503 Service Unavailable

```json theme={null}
{
  "message_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "error",
  "response": "Channel not started"
}
```

The channel hasn't been initialized yet. Wait for IronClaw to start.

## Security

### Webhook Secret

The webhook secret is required and validated using constant-time comparison to prevent timing attacks:

```rust theme={null}
use subtle::ConstantTimeEq;

if !provided.as_bytes().ct_eq(expected.as_bytes()).into() {
    return Err("Invalid secret");
}
```

### Fixed User ID

Each HTTP channel instance has a fixed `user_id` from config. The `user_id` field in requests is ignored:

```rust theme={null}
let msg = IncomingMessage::new("http", &self.config.user_id, &req.content);
```

This prevents impersonation attacks.

### Rate Limiting

The channel enforces a sliding window rate limit:

* **60 requests/minute** - Resets every 60 seconds
* **100 pending sync requests** - Prevents resource exhaustion

Exceeding these returns HTTP 429.

## Integration Examples

### cURL

```bash theme={null}
curl -X POST http://localhost:8080/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from cURL",
    "secret": "your-webhook-secret"
  }'
```

### Python

```python theme={null}
import requests

response = requests.post(
    "http://localhost:8080/webhook",
    json={
        "content": "Hello from Python",
        "secret": "your-webhook-secret",
        "wait_for_response": True
    }
)

print(response.json()["response"])
```

### JavaScript (Node.js)

```javascript theme={null}
const fetch = require('node-fetch');

const response = await fetch('http://localhost:8080/webhook', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    content: 'Hello from Node.js',
    secret: 'your-webhook-secret',
    wait_for_response: true
  })
});

const data = await response.json();
console.log(data.response);
```

### Bash Script

```bash theme={null}
#!/bin/bash

SECRET="your-webhook-secret"
URL="http://localhost:8080/webhook"

send_message() {
  local content="$1"
  curl -s -X POST "$URL" \
    -H "Content-Type: application/json" \
    -d "{
      \"content\": \"$content\",
      \"secret\": \"$SECRET\",
      \"wait_for_response\": true
    }" | jq -r '.response'
}

# Usage
send_message "What is the weather in Tokyo?"
```

## Use Cases

### CI/CD Integration

Notify the agent when builds fail:

```yaml theme={null}
# .github/workflows/ci.yml
steps:
  - name: Notify IronClaw on failure
    if: failure()
    run: |
      curl -X POST http://ironclaw.internal:8080/webhook \
        -H "Content-Type: application/json" \
        -d '{
          "content": "Build failed: ${{ github.repository }}#${{ github.run_number }}",
          "secret": "${{ secrets.IRONCLAW_SECRET }}"
        }'
```

### Monitoring Alerts

Forward alerts from monitoring systems:

```python theme={null}
# Prometheus Alertmanager webhook receiver
from flask import Flask, request
import requests

app = Flask(__name__)

@app.route('/alertmanager', methods=['POST'])
def alertmanager():
    alerts = request.json['alerts']
    for alert in alerts:
        requests.post('http://localhost:8080/webhook', json={
            'content': f"Alert: {alert['labels']['alertname']} - {alert['annotations']['summary']}",
            'secret': 'your-webhook-secret'
        })
    return '', 200
```

### Slack Slash Commands

Bridge Slack slash commands to IronClaw:

```python theme={null}
# Slack slash command webhook
from flask import Flask, request
import requests

app = Flask(__name__)

@app.route('/slack/ironclaw', methods=['POST'])
def slack_command():
    text = request.form['text']
    response = requests.post('http://localhost:8080/webhook', json={
        'content': text,
        'secret': 'your-webhook-secret',
        'wait_for_response': True
    })
    return response.json()['response']
```

### Chatbot Gateway

Proxy multiple chat platforms through the HTTP channel:

```python theme={null}
# Multi-platform chatbot
import requests

def send_to_ironclaw(message, platform, user_id):
    response = requests.post('http://localhost:8080/webhook', json={
        'content': f"[{platform}:{user_id}] {message}",
        'secret': 'your-webhook-secret',
        'wait_for_response': True,
        'thread_id': f"{platform}-{user_id}"
    })
    return response.json()['response']

# Usage
reply = send_to_ironclaw("Hello", "discord", "user123")
print(reply)
```

## Source Code

* **Implementation:** `~/workspace/source/src/channels/http.rs`
* **Tests:** `~/workspace/source/src/channels/http.rs:359-445`

## Troubleshooting

### 401 Unauthorized

* Verify `HTTP_WEBHOOK_SECRET` matches the secret in requests
* Check for typos in the secret value
* Ensure the secret is set before starting IronClaw

### 503 Service Unavailable

* Wait for IronClaw to fully start
* Check logs for "HTTP channel ready"
* Verify `HTTP_PORT` is correct

### 429 Too Many Requests

* Reduce request frequency to less than 60/minute
* Implement exponential backoff in client
* Close stale wait-for-response requests

### Timeout on wait\_for\_response

* Requests timeout after 60 seconds
* Complex queries may exceed this; use async mode instead
* Check agent logs for errors during processing

### Empty response field

* `response` field is only present when `wait_for_response: true`
* For async requests, use Web Gateway SSE or WebSocket to receive responses
