Simple Agent
Docsapi

Webhooks

Receive real-time events — new conversation, lead captured, handoff requested — with HMAC verification.

Webhooks push Simple Agent events to your server in real time. Use them to sync leads with your CRM, trigger automations, or log conversations in your system.

Set up a webhook

  1. Dashboard → Settings → Webhooks → Add endpoint
  2. Enter your server URL
  3. Select the events you want to receive
  4. Save — Simple Agent sends a verification event immediately

Available events

Event When it fires
conversation.created User's first message
conversation.ended Conversation ended (timeout or user closed)
message.received Each user message
message.sent Each agent response
lead.captured User's email collected via an Action
handoff.requested User requested human support
source.indexed Training source indexed successfully
source.failed Source indexing failed

Payload format

{
  "event": "lead.captured",
  "id": "evt_01hwxyz...",
  "created_at": "2026-05-14T10:30:00Z",
  "agent_id": "ag_xxx",
  "data": {
    "lead": {
      "email": "user@example.com",
      "name": "Jane Smith",
      "phone": "+15551234567"
    },
    "conversation_id": "conv_xxx",
    "source_channel": "widget",
    "session_locale": "en"
  }
}

HMAC verification

Simple Agent signs each request with HMAC-SHA256 using the Webhook Secret generated in the dashboard. You must verify the signature to reject unauthorized requests.

The header sent:

X-Simple Agent-Signature: sha256=a4b7c3d2e1f0...
X-Simple Agent-Timestamp: 1715686800

Verify in Node.js

import crypto from "crypto";

function verifyWebhook(
  payload: string,        // raw body string (not parsed)
  signature: string,      // X-Simple Agent-Signature header
  timestamp: string,      // X-Simple Agent-Timestamp header
  secret: string          // your Webhook Secret
): boolean {
  // Reject requests older than 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) return false;

  const signedPayload = `${timestamp}.${payload}`;
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// In Express:
app.post("/webhook/simple-agent", express.raw({ type: "application/json" }), (req, res) => {
  const isValid = verifyWebhook(
    req.body.toString(),
    req.headers["x-simpleagent-signature"] as string,
    req.headers["x-simpleagent-timestamp"] as string,
    process.env.SIMPLE AGENT_WEBHOOK_SECRET!
  );

  if (!isValid) return res.status(401).json({ error: "Invalid signature" });

  const event = JSON.parse(req.body.toString());
  // process event...

  res.status(200).json({ received: true });
});

Verify in Python

import hmac
import hashlib
import time

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    # Reject old requests (>5 min)
    if abs(time.time() - int(timestamp)) > 300:
        return False

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

    return hmac.compare_digest(expected, signature)

Expected response

Your endpoint must return HTTP 200 within 10 seconds. If it doesn't return 2xx, Simple Agent considers it a failure and retries.

Retry policy:

Attempt Delay
1st retry 1 minute
2nd retry 5 minutes
3rd retry 30 minutes
4th retry 2 hours
5th retry 8 hours

After 5 failed attempts, the event is discarded and appears as failed in the webhook log.


Delivery log

In the dashboard → Settings → Webhooks → View deliveries, you can see:

  • Status of each delivery (success / failure)
  • Sent payload
  • Your server's response
  • Resend button for failures

Resend manually via API

POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry

curl -X POST https://simple-agent.me/api/v1/webhooks/wh_xxx/deliveries/del_xxx/retry \
  -H "Authorization: Bearer af_live_xxx"

Security tips

  • Never rely on IP alone — always validate the HMAC signature
  • Use timingSafeEqual to compare signatures — prevents timing attacks
  • Verify the timestamp — reject events older than 5 minutes to prevent replay attacks
  • Save event.id — ignore events with an already-processed ID (idempotency)

Rate Limits → · Errors → · Actions →