Skip to main content

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.

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

VariableTypeDefaultDescription
HTTP_HOSTstring"0.0.0.0"Bind address
HTTP_PORTinteger8080HTTP port
HTTP_WEBHOOK_SECRETstringrequiredSecret for request authentication

Endpoints

Health Check

GET /health
Response:
{
  "status": "healthy",
  "channel": "http"
}

Send Message (Async)

POST /webhook
Content-Type: application/json

{
  "content": "Hello, agent!",
  "secret": "your-webhook-secret"
}
Response (202 Accepted):
{
  "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)

POST /webhook
Content-Type: application/json

{
  "content": "What is 2+2?",
  "secret": "your-webhook-secret",
  "wait_for_response": true
}
Response (200 OK):
{
  "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

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

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

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

{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Invalid webhook secret"
}

401 Unauthorized - Missing Secret

{
  "message_id": "00000000-0000-0000-0000-000000000000",
  "status": "error",
  "response": "Webhook secret required"
}

413 Payload Too Large

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

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

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

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

Python

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)

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

#!/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:
# .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:
# 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:
# 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:
# 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

Build docs developers (and LLMs) love