9 min read Updated May 9, 2026

Webhooks

Webhooks enable real-time notifications when events occur in Tamandua. You can configure outbound webhooks to push alerts to external systems, and inbound webhooks to receive data from SIEM/SOAR platforms.

Outbound Webhooks

Webhook Events

Tamandua can send webhooks for the following event types:

EventDescription
alert.createdNew alert generated
alert.updatedAlert status changed
alert.resolvedAlert marked as resolved
alert.assignedAlert assigned to user
agent.connectedAgent came online
agent.disconnectedAgent went offline
agent.isolatedAgent network isolated
response.executedResponse action completed
detection.new_threatNew threat type detected
integration.errorIntegration failure

Configure Webhooks via API

Create Webhook Integration

Endpoint: POST /api/v1/integrations
curl -X POST "https://api.tamandua.io/api/v1/integrations" \
  -H "Authorization: Bearer tam_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "webhook",
    "name": "Security Team Alerts",
    "description": "Send critical alerts to security team webhook",
    "enabled": true,
    "config": {
      "url": "https://your-server.com/webhook/tamandua",
      "secret": "your-webhook-secret-here",
      "events": ["alert.created", "alert.resolved"],
      "severity_filter": ["critical", "high"],
      "headers": {
        "X-Custom-Header": "custom-value"
      },
      "retry_count": 3,
      "timeout_seconds": 30
    }
  }'
Response:
{
  "data": {
    "id": "int-550e8400-e29b-41d4-a716-446655440000",
    "type": "webhook",
    "name": "Security Team Alerts",
    "description": "Send critical alerts to security team webhook",
    "enabled": true,
    "config": {
      "url": "https://your-server.com/webhook/tamandua",
      "secret": "**********************",
      "events": ["alert.created", "alert.resolved"],
      "severity_filter": ["critical", "high"]
    },
    "created_at": "2024-01-15T10:00:00Z"
  }
}

List Webhooks

Endpoint: GET /api/v1/integrations?type=webhook
curl -X GET "https://api.tamandua.io/api/v1/integrations?type=webhook" \
  -H "Authorization: Bearer tam_live_xxxxx"

Update Webhook

Endpoint: PUT /api/v1/integrations/:id
curl -X PUT "https://api.tamandua.io/api/v1/integrations/int-550e8400" \
  -H "Authorization: Bearer tam_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": false,
    "config": {
      "severity_filter": ["critical"]
    }
  }'

Delete Webhook

Endpoint: DELETE /api/v1/integrations/:id
curl -X DELETE "https://api.tamandua.io/api/v1/integrations/int-550e8400" \
  -H "Authorization: Bearer tam_live_xxxxx"

Test Webhook

Endpoint: POST /api/v1/integrations/:id/test
curl -X POST "https://api.tamandua.io/api/v1/integrations/int-550e8400/test" \
  -H "Authorization: Bearer tam_live_xxxxx"
Response:
{
  "success": true,
  "message": "Connection successful",
  "details": {
    "status_code": 200,
    "response_time_ms": 150
  }
}

Webhook Payload Format

Alert Created Event

{
  "event": "alert.created",
  "timestamp": "2024-01-15T14:30:00Z",
  "webhook_id": "int-550e8400",
  "data": {
    "alert": {
      "id": "alert-001",
      "title": "Ransomware behavior detected",
      "description": "Mass file encryption activity detected",
      "severity": "critical",
      "status": "open",
      "threat_score": 95,
      "agent_id": "agent-001",
      "agent_hostname": "WORKSTATION-01",
      "mitre_tactics": ["impact"],
      "mitre_techniques": ["T1486"],
      "evidence": {
        "files_encrypted": 150,
        "process_name": "unknown.exe"
      },
      "created_at": "2024-01-15T14:30:00Z"
    }
  }
}

Alert Updated Event

{
  "event": "alert.updated",
  "timestamp": "2024-01-15T15:00:00Z",
  "webhook_id": "int-550e8400",
  "data": {
    "alert": {
      "id": "alert-001",
      "title": "Ransomware behavior detected",
      "severity": "critical",
      "status": "investigating",
      "previous_status": "open",
      "assigned_to": {
        "id": "user-123",
        "name": "John Doe",
        "email": "john@example.com"
      },
      "updated_at": "2024-01-15T15:00:00Z"
    },
    "changes": {
      "status": ["open", "investigating"],
      "assigned_to_id": [null, "user-123"]
    }
  }
}

Agent Disconnected Event

{
  "event": "agent.disconnected",
  "timestamp": "2024-01-15T14:35:00Z",
  "webhook_id": "int-550e8400",
  "data": {
    "agent": {
      "id": "agent-001",
      "hostname": "WORKSTATION-01",
      "os_type": "windows",
      "last_seen": "2024-01-15T14:34:55Z",
      "disconnect_reason": "timeout"
    }
  }
}

Response Executed Event

{
  "event": "response.executed",
  "timestamp": "2024-01-15T14:31:00Z",
  "webhook_id": "int-550e8400",
  "data": {
    "response": {
      "id": "resp-001",
      "type": "rapid_response",
      "agent_id": "agent-001",
      "agent_hostname": "WORKSTATION-01",
      "alert_id": "alert-001",
      "actions": [
        {"action": "kill_process", "pid": 1234, "result": "success"},
        {"action": "quarantine_file", "path": "C:\\malware.exe", "result": "success"},
        {"action": "isolate_network", "result": "success"}
      ],
      "automated": true,
      "executed_by": null,
      "executed_at": "2024-01-15T14:31:00Z",
      "duration_ms": 250
    }
  }
}

Signature Verification

All outbound webhooks include an HMAC signature for verification. The signature is computed using SHA-256 and included in the X-Tamandua-Signature header.

Signature Format

X-Tamandua-Signature: sha256=<hex-encoded-signature>
X-Tamandua-Timestamp: 1705329000

Verification Example (Python)

import hmac
import hashlib
import time

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    # Check timestamp is within 5 minutes
    if abs(time.time() - int(timestamp)) > 300:
        return False

    # Compute expected signature
    signed_payload = f"{timestamp}.{payload.decode()}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Compare signatures (constant-time)
    received = signature.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

# Usage
is_valid = verify_webhook(
    payload=request.body,
    signature=request.headers["X-Tamandua-Signature"],
    timestamp=request.headers["X-Tamandua-Timestamp"],
    secret="your-webhook-secret"
)

Verification Example (Node.js)

const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
  // Check timestamp is within 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false;
  }

  // Compute expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Compare signatures (constant-time)
  const received = signature.replace('sha256=', '');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}

Retry Policy

Failed webhook deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook is marked as failed and the integration is flagged for review.

Response Requirements

Your webhook endpoint should:

  • Return HTTP 2xx status code for success
  • Respond within 30 seconds (configurable)
  • Handle duplicate deliveries idempotently

Inbound Webhooks

Receive webhooks from external SIEM/SOAR platforms.

Supported Sources

SourceDescription
splunkSplunk SOAR/Enterprise Security
sentinelMicrosoft Sentinel
qradarIBM QRadar
pagerdutyPagerDuty incidents
slackSlack interactive messages
genericGeneric webhook format

Inbound Webhook Endpoint

Endpoint: POST /api/v1/integrations/webhook/:source

Splunk Webhook Example

curl -X POST "https://api.tamandua.io/api/v1/integrations/webhook/splunk" \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Signature: sha256=abc123..." \
  -d '{
    "alert_name": "Suspicious Process",
    "severity": "high",
    "source_ip": "10.0.0.50",
    "dest_ip": "192.168.1.100",
    "raw_event": "..."
  }'

PagerDuty Webhook Example

curl -X POST "https://api.tamandua.io/api/v1/integrations/webhook/pagerduty" \
  -H "Content-Type: application/json" \
  -H "X-PagerDuty-Signature: v1=abc123..." \
  -d '{
    "event": {
      "event_type": "incident.triggered",
      "incident": {
        "id": "P123ABC",
        "title": "Critical Alert",
        "urgency": "high"
      }
    }
  }'

Inbound Signature Verification

Inbound webhooks are verified using the configured secret for each source. The secret is stored server-side and never exposed in requests.

Supported Signature Headers:
  • X-Hub-Signature-256 (GitHub style)
  • X-Webhook-Signature
  • X-Signature
  • X-Tines-Signature
  • X-Slack-Signature

Configure Inbound Webhook Secrets

Set secrets via environment variables or application config:

# Environment variables
WEBHOOK_SECRET_SPLUNK=your-splunk-secret
WEBHOOK_SECRET_PAGERDUTY=your-pagerduty-secret
WEBHOOK_SECRET_GENERIC=your-generic-secret

Webhook History

View inbound webhook history:

Endpoint: GET /api/v1/integrations/webhook/history Query Parameters:
ParameterTypeDescription
sourcestringFilter by source
statusstringFilter by status: success, error
limitintegerMax results (default: 100)
offsetintegerSkip results
Example Response:
{
  "data": [
    {
      "id": "wh-001",
      "source": "splunk",
      "status": "success",
      "payload_size": 1520,
      "duration_ms": 45,
      "error": null,
      "timestamp": "2024-01-15T14:30:00Z"
    },
    {
      "id": "wh-002",
      "source": "pagerduty",
      "status": "error",
      "payload_size": 890,
      "duration_ms": 150,
      "error": "Invalid signature",
      "timestamp": "2024-01-15T14:25:00Z"
    }
  ],
  "meta": {
    "total": 1520,
    "limit": 100,
    "offset": 0
  }
}

Routing Rules

Configure rules to route alerts to specific integrations based on conditions.

List Routing Rules

Endpoint: GET /api/v1/integrations/rules

Create Routing Rule

Endpoint: POST /api/v1/integrations/rules
curl -X POST "https://api.tamandua.io/api/v1/integrations/rules" \
  -H "Authorization: Bearer tam_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Critical to PagerDuty",
    "description": "Route critical alerts to PagerDuty",
    "conditions": [
      {"field": "severity", "operator": "eq", "value": "critical"},
      {"field": "status", "operator": "eq", "value": "open"}
    ],
    "destinations": ["pagerduty-integration-id"],
    "enabled": true,
    "priority": 10
  }'
Response:
{
  "data": {
    "id": "rule-001",
    "name": "Critical to PagerDuty",
    "conditions": [
      {"field": "severity", "operator": "eq", "value": "critical"},
      {"field": "status", "operator": "eq", "value": "open"}
    ],
    "destinations": ["pagerduty-integration-id"],
    "enabled": true,
    "priority": 10
  }
}

Condition Operators

OperatorDescriptionExample
eqEquals{"field": "severity", "operator": "eq", "value": "critical"}
neNot equals{"field": "status", "operator": "ne", "value": "resolved"}
inIn list{"field": "severity", "operator": "in", "value": ["critical", "high"]}
containsContains substring{"field": "title", "operator": "contains", "value": "ransomware"}
matchesRegex match{"field": "hostname", "operator": "matches", "value": "^DC-.*"}
gt, gte, lt, lteNumeric comparison{"field": "threat_score", "operator": "gte", "value": 80}

Test Routing

Test which rules would match an alert:

Endpoint: POST /api/v1/integrations/rules/test
curl -X POST "https://api.tamandua.io/api/v1/integrations/rules/test" \
  -H "Authorization: Bearer tam_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "alert": {
      "severity": "critical",
      "status": "open",
      "title": "Ransomware detected",
      "threat_score": 95
    }
  }'
Response:
{
  "data": {
    "matched_rules": [
      {
        "id": "rule-001",
        "name": "Critical to PagerDuty",
        "destinations": ["pagerduty-integration-id"]
      }
    ],
    "destinations": ["pagerduty-integration-id"]
  }
}

Integration Statistics

Endpoint: GET /api/v1/integrations/stats
{
  "data": {
    "alerts_routed": 1520,
    "rules_matched": 1450,
    "destinations_triggered": 2100,
    "errors": 12,
    "by_destination": {
      "webhook-001": {"success": 500, "failed": 2},
      "pagerduty-001": {"success": 300, "failed": 5}
    },
    "by_rule": {
      "rule-001": 800,
      "rule-002": 650
    },
    "last_activity": "2024-01-15T14:30:00Z"
  }
}

Integration Logs

View detailed logs for integration activity:

Endpoint: GET /api/v1/integrations/logs Query Parameters:
ParameterTypeDescription
integration_namestringFilter by integration
statusstringFilter by status
actionstringFilter by action
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)
limitintegerMax results
offsetintegerSkip results
summarybooleanReturn aggregated summary
Example Response:
{
  "data": [
    {
      "id": "log-001",
      "integration_name": "pagerduty",
      "action": "create_incident",
      "status": "success",
      "request_body": {...},
      "response_body": {...},
      "error_message": null,
      "duration_ms": 250,
      "inserted_at": "2024-01-15T14:30:00Z"
    }
  ],
  "meta": {
    "total": 1520,
    "limit": 100,
    "offset": 0
  }
}

Pre-built Integrations

Tamandua includes pre-built integrations for common platforms:

Slack

{
  "type": "slack",
  "config": {
    "webhook_url": "https://hooks.slack.com/services/...",
    "channel": "#security-alerts",
    "username": "Tamandua",
    "icon_emoji": ":shield:"
  }
}

Microsoft Teams

{
  "type": "teams",
  "config": {
    "webhook_url": "https://outlook.office.com/webhook/..."
  }
}

PagerDuty

{
  "type": "pagerduty",
  "config": {
    "routing_key": "your-pagerduty-routing-key",
    "severity_mapping": {
      "critical": "critical",
      "high": "error",
      "medium": "warning",
      "low": "info"
    }
  }
}

Splunk HEC

{
  "type": "splunk",
  "config": {
    "hec_url": "https://splunk.example.com:8088",
    "hec_token": "your-hec-token",
    "index": "security",
    "source": "tamandua",
    "sourcetype": "_json"
  }
}

ServiceNow

{
  "type": "servicenow",
  "config": {
    "instance_url": "https://your-instance.service-now.com",
    "username": "api_user",
    "password": "api_password",
    "table": "incident"
  }
}