Skip to content

Webhooks ​

AgentGate can notify external systems when request events occur via webhooks.

Features ​

  • 🔔 Real-time event notifications
  • 🔐 HMAC-SHA256 signature verification
  • 🔄 Automatic retries with exponential backoff
  • 📋 Comprehensive event types

Creating Webhooks ​

Via API ​

bash
curl -X POST http://localhost:3000/api/webhooks \
  -H "Authorization: Bearer $AGENTGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["request.created", "request.decided"],
    "secret": "your-signing-secret"
  }'

Via Dashboard ​

  1. Open the AgentGate dashboard
  2. Navigate to Settings → Webhooks
  3. Click Add Webhook
  4. Enter URL, select events, and optionally set a secret

Webhook Events ​

EventDescription
request.createdA new approval request was created
request.decidedA request was approved or denied
request.expiredA request expired without decision
request.confirmedAction execution was confirmed

Event Payload ​

All webhooks follow this structure:

json
{
  "event": "request.decided",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "id": "req_abc123",
    "action": "send_email",
    "status": "approved",
    "params": {
      "to": "customer@example.com"
    },
    "decidedBy": "admin@example.com",
    "decisionReason": "Looks good",
    "createdAt": "2024-01-15T10:25:00Z",
    "decidedAt": "2024-01-15T10:30:00Z"
  }
}

Signature Verification ​

If you provide a secret, requests are signed with HMAC-SHA256.

Headers ​

X-AgentGate-Signature: sha256=abc123...
X-AgentGate-Timestamp: 1705312200

Verification Example ​

typescript
import crypto from 'crypto'

function verifyWebhook(
  payload: string,
  signature: string,
  timestamp: string,
  secret: string
): boolean {
  const signedPayload = `${timestamp}.${payload}`
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex')
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expectedSignature}`)
  )
}

// Express middleware
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-agentgate-signature'] as string
  const timestamp = req.headers['x-agentgate-timestamp'] as string
  
  if (!verifyWebhook(req.body.toString(), signature, timestamp, SECRET)) {
    return res.status(401).send('Invalid signature')
  }
  
  const event = JSON.parse(req.body.toString())
  // Handle event...
  
  res.status(200).send('OK')
})
python
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    signed_payload = f"{timestamp}.{payload.decode()}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, f"sha256={expected}")

# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-AgentGate-Signature')
    timestamp = request.headers.get('X-AgentGate-Timestamp')
    
    if not verify_webhook(request.data, signature, timestamp, SECRET):
        return 'Invalid signature', 401
    
    event = request.json
    # Handle event...
    
    return 'OK', 200

Retry Behavior ​

Failed webhook deliveries are retried by a DB-based retry scanner that runs every 30 seconds. The delay between attempts uses exponential backoff with the formula 2^attempt × 1000ms:

AttemptBackoff Delay
1 (initial)Immediate
2 (1st retry)~2 seconds (2^1 × 1000ms)
3 (2nd retry)~4 seconds (2^2 × 1000ms)

After 3 failed attempts, the delivery is marked as permanently failed and no further retries occur. Note: actual retry timing depends on the scanner interval (30s), so retries may be delayed up to 30 seconds beyond the computed backoff.

Configuration ​

VariableDefaultDescription
WEBHOOK_TIMEOUT_MS5000Request timeout in ms
WEBHOOK_MAX_RETRIES3Maximum retry attempts

Managing Webhooks ​

List webhooks ​

bash
curl http://localhost:3000/api/webhooks \
  -H "Authorization: Bearer $AGENTGATE_API_KEY"

Delete webhook ​

bash
curl -X DELETE http://localhost:3000/api/webhooks/whk_abc123 \
  -H "Authorization: Bearer $AGENTGATE_API_KEY"

Update webhook ​

bash
curl -X PATCH http://localhost:3000/api/webhooks/whk_abc123 \
  -H "Authorization: Bearer $AGENTGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["request.created", "request.decided", "request.expired"],
    "enabled": true
  }'

Use Cases ​

Audit Logging ​

Send all events to your logging system:

json
{
  "url": "https://logs.example.com/agentgate",
  "events": ["request.created", "request.decided", "request.expired"],
  "secret": "audit-secret"
}

Workflow Automation ​

Trigger actions when requests are approved:

javascript
// n8n, Zapier, or custom webhook handler
app.post('/webhook', (req, res) => {
  const { event, data } = req.body
  
  if (event === 'request.decided' && data.status === 'approved') {
    // Trigger downstream automation
    executeApprovedAction(data)
  }
  
  res.sendStatus(200)
})

Slack/Discord Notifications ​

While AgentGate has built-in Slack/Discord support, you can also use webhooks with services like Zapier to create custom notification flows.

Released under the MIT License.