Architecture

AI Agent Webhooks

What are AI agent webhooks?

AI agent webhooks connect external events to agent behavior. When something happens in an external system—an email arrives, a form is submitted, a payment succeeds—a webhook fires and triggers the agent to respond.

The pattern transforms agents from reactive (waiting for human input) to event-driven (responding to the world):

External Event: New email arrives
       ↓
Webhook fires: POST /webhook/email
       ↓
Agent triggered: "New email from Sarah about Project Phoenix"
       ↓
Agent acts: Reads email, updates project notes, notifies user if urgent

Webhooks are the nervous system connecting agents to the external world.

How agent webhooks work

The basic flow

  1. External service configured to send webhooks to agent endpoint
  2. Event occurs in external service
  3. External service sends HTTP POST to agent endpoint
  4. Agent system receives webhook payload
  5. Payload processed and routed to appropriate agent
  6. Agent handles the event, takes action if needed

Example: Email webhook

Gmail → Webhook → Agent System → Email Agent

Webhook payload:
{
  "type": "email.received",
  "from": "sarah@company.com",
  "subject": "Project Phoenix Update",
  "snippet": "Here's the latest on the timeline...",
  "timestamp": "2024-01-15T10:30:00Z"
}

Agent receives and processes:
"New email from Sarah about Project Phoenix. 
 Marking as priority, updating project notes."

Why agents need webhooks

Real-time responsiveness

Instead of polling ("check email every 5 minutes"), agents respond immediately when things happen:

  • Email arrives → instant notification
  • Form submitted → immediate processing
  • Payment received → instant confirmation

External system integration

Webhooks connect agents to any service that supports them:

  • Email providers (Gmail, Outlook)
  • CRMs (Salesforce, HubSpot)
  • Communication (Slack, Discord)
  • Payments (Stripe, PayPal)
  • Forms (Typeform, Tally)
  • Custom applications

Event-driven architecture

Webhooks enable event-driven patterns:

  • Loose coupling between systems
  • Scalable processing
  • Clear event boundaries
  • Easy debugging (each event is discrete)

Reduced polling overhead

Heartbeats and cron jobs work, but polling has costs:

  • API rate limits
  • Wasted computation
  • Delay between event and detection

Webhooks are immediate and efficient.

Common webhook integrations

Email webhooks

trigger: email.received
conditions:
  - from_vip_sender
  - or: contains_urgent_keywords
actions:
  - notify_user
  - add_to_task_list
  - auto_respond_if_appropriate

Calendar webhooks

trigger: calendar.event_reminder
conditions:
  - event_in_30_minutes
actions:
  - send_reminder
  - prepare_meeting_notes
  - check_conflicts

Form submission webhooks

trigger: form.submitted
source: contact_form
actions:
  - qualify_lead
  - send_to_crm
  - schedule_followup
  - notify_sales_team

Payment webhooks

trigger: payment.succeeded
source: stripe
actions:
  - send_confirmation_email
  - provision_access
  - update_customer_record
  - notify_support_if_high_value

Repository webhooks

trigger: github.push
conditions:
  - branch: main
actions:
  - summarize_changes
  - check_for_issues
  - update_changelog

Implementing agent webhooks

Endpoint setup

Create a webhook receiver:

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook/<source>', methods=['POST'])
def handle_webhook(source):
    payload = request.json
    signature = request.headers.get('X-Signature')
    
    # Verify webhook authenticity
    if not verify_signature(source, payload, signature):
        return {'error': 'Invalid signature'}, 401
    
    # Route to appropriate agent
    agent_session = get_agent_for_source(source)
    agent_session.handle_event({
        'source': source,
        'payload': payload
    })
    
    return {'status': 'received'}, 200

Webhook configuration

Define how webhooks map to agent behavior:

# webhooks.yaml
webhooks:
  email:
    endpoint: /webhook/email
    secret: ${EMAIL_WEBHOOK_SECRET}
    agent: email-agent
    prompt_template: |
      New email received:
      From: {{from}}
      Subject: {{subject}}
      Preview: {{snippet}}
      
      Evaluate and take appropriate action.
      
  stripe:
    endpoint: /webhook/stripe
    secret: ${STRIPE_WEBHOOK_SECRET}
    agent: billing-agent
    events:
      - payment_intent.succeeded
      - customer.subscription.created
      - customer.subscription.deleted

Security considerations

def verify_signature(source, payload, signature):
    """Verify webhook comes from legitimate source"""
    secret = get_webhook_secret(source)
    expected = compute_signature(payload, secret)
    return hmac.compare_digest(signature, expected)

Always verify webhook signatures to prevent spoofing.

Webhook patterns

The router pattern

Single endpoint routes to multiple agents:

/webhook → Router → Email Agent
                  → Calendar Agent
                  → Payment Agent
                  → Form Agent
def route_webhook(payload):
    event_type = payload.get('type')
    
    routing = {
        'email.*': 'email-agent',
        'calendar.*': 'calendar-agent',
        'payment.*': 'billing-agent',
    }
    
    for pattern, agent in routing.items():
        if matches(event_type, pattern):
            return get_agent(agent)

The filter pattern

Process only relevant webhooks:

filters:
  email:
    - from_in: [vip_list]
    - subject_contains: [urgent, asap, important]
    - not_from: [no-reply@*, marketing@*]

The transformation pattern

Normalize different webhook formats:

def transform_webhook(source, payload):
    """Convert various formats to standard event"""
    
    if source == 'gmail':
        return {
            'type': 'email.received',
            'from': payload['sender'],
            'subject': payload['subject'],
            'body': payload['snippet']
        }
    elif source == 'outlook':
        return {
            'type': 'email.received',
            'from': payload['from']['emailAddress']['address'],
            'subject': payload['subject'],
            'body': payload['bodyPreview']
        }

The queue pattern

Buffer webhooks for processing:

Webhook → Queue → Worker Agent(s)

Handles spikes without overwhelming agents.

Webhook + heartbeat combination

Webhooks for immediate events, heartbeats for periodic awareness:

Webhooks:
  - Urgent emails → immediate notification
  - Form submissions → immediate processing
  - Critical alerts → immediate response

Heartbeats:
  - Check for non-urgent emails
  - Review calendar for upcoming events
  - Summarize activity since last check

The combination provides both responsiveness and awareness.

Common webhook challenges

Challenge: Delivery failures

Webhooks can fail to deliver.

Solution:

  • Implement retry logic on sender side
  • Queue webhooks on receiver side
  • Idempotent handlers (safe to process twice)
  • Dead letter queues for failed webhooks

Challenge: Out-of-order delivery

Webhooks may arrive out of sequence.

Solution:

  • Include timestamps
  • Track event sequences
  • Design handlers to tolerate reordering

Challenge: Duplicate delivery

Same webhook may be sent multiple times.

Solution:

  • Track processed webhook IDs
  • Idempotent handlers
  • Deduplication layer

Challenge: Volume spikes

Many webhooks arriving simultaneously.

Solution:

  • Queue incoming webhooks
  • Rate limit processing
  • Horizontal scaling
  • Backpressure mechanisms

Best practices

Always verify signatures

Never trust webhook data without cryptographic verification. Attackers can spoof webhooks.

Respond quickly

Return 200 immediately, process asynchronously. Slow responses cause timeouts and retries.

@app.route('/webhook', methods=['POST'])
def webhook():
    # Queue for async processing
    queue.put(request.json)
    return '', 200  # Respond immediately

Log everything

Every webhook should be logged:

[10:30:00] Received webhook: email.received
[10:30:00] From: sarah@company.com
[10:30:00] Routed to: email-agent
[10:30:01] Processing complete

Handle failures gracefully

Webhook processing will fail. Have fallbacks:

  • Queue for retry
  • Notify operators
  • Degrade gracefully

Test with realistic payloads

Use actual webhook examples during development. Formats vary and documentation isn't always complete.