Skip to main content

Webhooks

Webhooks allow you to receive real-time HTTP POST notifications when events occur in Mission Control. Configure webhook endpoints to integrate with external services, trigger automation, or monitor system activity.

Features

  • Event filtering: Subscribe to specific event types or all events (*)
  • HMAC signature verification: Cryptographically signed payloads using SHA-256
  • Automatic retries: Exponential backoff with jitter (30s, 5m, 30m, 2h, 8h)
  • Circuit breaker: Auto-disable after 5 consecutive failures
  • Delivery history: Track all webhook attempts with status codes and response bodies

List Webhooks

curl http://localhost:3000/api/webhooks \
  -H "x-api-key: YOUR_API_KEY"

Response

webhooks
array
Array of webhook configurations with delivery statistics
id
integer
Webhook ID
name
string
Webhook name
url
string
Target URL for webhook deliveries
secret
string
Masked secret (shows only last 4 characters: ••••••abc1)
events
array
Array of subscribed event types (e.g., ["agent.status_change", "activity.task_created"] or ["*"] for all)
enabled
boolean
Whether webhook is active
consecutive_failures
integer
Number of consecutive delivery failures
circuit_open
boolean
true if circuit breaker is tripped (≥5 failures)
total_deliveries
integer
Total delivery attempts
successful_deliveries
integer
Deliveries with 2xx status code
failed_deliveries
integer
Deliveries with errors or non-2xx status

Create Webhook

curl -X POST http://localhost:3000/api/webhooks \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "name": "Slack Notifications",
    "url": "https://hooks.slack.com/services/T00/B00/XXX",
    "events": ["agent.error", "activity.task_created"],
    "generate_secret": true
  }'

Request Body

name
string
required
Webhook name for identification
url
string
required
Target URL (must be valid HTTP/HTTPS endpoint)
events
array
Event types to subscribe to. Use ["*"] for all events. Defaults to ["*"]
generate_secret
boolean
default:true
Generate HMAC secret for signature verification

Response

The secret field is only shown in full once during creation. Save it securely.
id
integer
Created webhook ID
secret
string
HMAC secret (64-character hex string). Only shown on creation.
message
string
Confirmation message

Update Webhook

curl -X PUT http://localhost:3000/api/webhooks \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "id": 1,
    "name": "Updated Name",
    "events": ["*"],
    "enabled": true,
    "reset_circuit": true
  }'

Request Body

id
integer
required
Webhook ID to update
name
string
New webhook name
url
string
New target URL
events
array
Updated event subscriptions
enabled
boolean
Enable or disable webhook
regenerate_secret
boolean
Generate a new HMAC secret (returns new secret in response)
reset_circuit
boolean
Reset circuit breaker (clears failures and re-enables webhook)

Delete Webhook

curl -X DELETE http://localhost:3000/api/webhooks \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"id": 1}'

Request Body

id
integer
required
Webhook ID to delete

Get Delivery History

Retrieve webhook delivery logs with status codes, error messages, and response bodies.
curl "http://localhost:3000/api/webhooks/deliveries?webhook_id=1&limit=50" \
  -H "x-api-key: YOUR_API_KEY"

Query Parameters

webhook_id
integer
Filter deliveries for a specific webhook
limit
integer
default:50
Maximum deliveries to return (max 200)
offset
integer
default:0
Pagination offset

Response

deliveries
array
id
integer
Delivery ID
webhook_id
integer
Parent webhook ID
event_type
string
Event type (e.g., agent.status_change, test.ping)
status_code
integer
HTTP status code (e.g., 200, 500, null if timeout)
error
string
Error message if delivery failed
duration_ms
number
Request duration in milliseconds
attempt
integer
Retry attempt number (0 = first attempt)
is_retry
boolean
Whether this is a retry of a previous failed delivery
created_at
integer
Unix timestamp
total
integer
Total delivery count

Retry Failed Delivery

Manually retry a failed webhook delivery.
curl -X POST http://localhost:3000/api/webhooks/retry \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"delivery_id": 123}'

Request Body

delivery_id
integer
required
ID of the failed delivery to retry

Response

success
boolean
Whether the retry succeeded
status_code
integer
HTTP status code from retry attempt
response_time_ms
number
Request duration

Test Webhook

Send a test ping to verify webhook configuration.
curl -X POST http://localhost:3000/api/webhooks/test \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"id": 1}'

Request Body

id
integer
required
Webhook ID to test

Test Payload Example

{
  "event": "test.ping",
  "timestamp": 1678901234,
  "data": {
    "message": "This is a test webhook from Mission Control",
    "webhook_id": 1,
    "webhook_name": "My Webhook",
    "triggered_by": "admin"
  }
}

Signature Verification

All webhook payloads include an X-MC-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing webhooks.

Verification Algorithm

  1. Extract the raw request body as a UTF-8 string (do not parse JSON first)
  2. Read the X-MC-Signature header
  3. Compute HMAC-SHA256 of the raw body using your webhook secret
  4. Format as: sha256=<hex-digest>
  5. Compare using constant-time comparison

Node.js Example

const crypto = require('crypto');

function verifySignature(secret, rawBody, signatureHeader) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  
  const sigBuf = Buffer.from(signatureHeader);
  const expBuf = Buffer.from(expected);
  
  if (sigBuf.length !== expBuf.length) return false;
  return crypto.timingSafeEqual(sigBuf, expBuf);
}

// Express middleware example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-mc-signature'];
  const rawBody = req.body.toString('utf8');
  
  if (!verifySignature(MY_SECRET, rawBody, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  const payload = JSON.parse(rawBody);
  // Process webhook...
  res.sendStatus(200);
});

Python Example

import hmac
import hashlib

def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected, signature_header)

# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-MC-Signature')
    raw_body = request.get_data()
    
    if not verify_signature(MY_SECRET, raw_body, signature):
        return 'Invalid signature', 401
    
    payload = request.get_json()
    # Process webhook...
    return '', 200

Retry Logic

Mission Control automatically retries failed webhook deliveries using exponential backoff:
AttemptDelayTotal Time
1immediate0s
230s ±20%~30s
35m ±20%~5m
430m ±20%~35m
52h ±20%~2.5h
68h ±20%~10.5h
Jitter (±20%) prevents thundering herd when multiple webhooks fail simultaneously.

Circuit Breaker

After 5 consecutive failures (across all retries), the webhook is automatically disabled. To re-enable:
  1. Fix the endpoint issue
  2. Call PUT /api/webhooks with reset_circuit: true

Event Types

Subscribe to specific event types or use * for all events:

Agent Events

  • agent.status_change - Agent status changed (online, offline, busy, error)
  • agent.error - Agent entered error state

Task Events

  • activity.task_created - New task created
  • activity.task_updated - Task fields modified
  • activity.task_deleted - Task deleted
  • activity.task_status_changed - Task status changed

Activity Events

  • activity.<type> - Generic activity (e.g., activity.agent_created, activity.user_login)

Notification Events

  • notification.<type> - System notifications

Security Events

  • security.<action> - Security-related events (e.g., security.login_failed)

Test Events

  • test.ping - Test webhook delivery

Payload Format

All webhook deliveries use this structure:
{
  "event": "agent.status_change",
  "timestamp": 1678901234,
  "data": {
    "id": 42,
    "name": "researcher",
    "status": "online",
    "previous_status": "offline"
  }
}

Headers

Content-Type: application/json
User-Agent: MissionControl-Webhook/1.0
X-MC-Event: agent.status_change
X-MC-Signature: sha256=abc123...

Best Practices

  1. Always verify signatures - Prevent spoofed webhooks
  2. Respond quickly - Return 200 OK within 10 seconds (timeout)
  3. Process async - Queue webhooks for background processing
  4. Idempotency - Handle duplicate deliveries gracefully (retries)
  5. Monitor failures - Alert on circuit breaker trips
  6. Rotate secrets - Use regenerate_secret periodically

Rate Limits

  • Creation/updates: 100 requests/minute per API key
  • Delivery timeout: 10 seconds per webhook
  • Max retries: 5 attempts with exponential backoff
  • History retention: Last 200 deliveries per webhook
The automatic retry scheduler runs every 60 seconds and processes up to 50 pending retries per batch.