Webhooks

Get notified in real-time when events happen in your AgentSend account — new emails received, deliveries confirmed, bounces detected, and more.

Overview

Instead of polling the API to check for new messages, webhooks let AgentSend push event data to your server the moment something happens. Register an HTTPS endpoint and AgentSend will send a POST request with a JSON payload whenever a subscribed event fires.

Webhooks are the recommended way to build reactive agents. When a reply lands in your agent's inbox, your application receives the full message payload within seconds and can act on it immediately.

Webhook endpoints must be publicly accessible over HTTPS. Localhost URLs are not accepted. Use a tool like ngrok or a tunnel service when developing locally.

Creating a Webhook

Register a new webhook by sending a POST request to /webhooks. You must provide a url field; all other fields are optional.

POST /webhooks
Parameter Type Description
url required string The HTTPS URL AgentSend will POST events to.
description string A human-readable label to identify the webhook in the dashboard.
events string[] List of event types to subscribe to. Omit to receive all events.
javascript
const res = await fetch("https://api.agentsend.io/webhooks", {
  method: "POST",
  headers: {
    "x-api-key": process.env.AGENTSEND_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://myapp.example.com/hooks/agentsend",
    description: "Production inbox events",
    events: ["message.received", "message.bounced"],
  }),
});

const webhook = await res.json();
console.log(webhook.id);     // wh_01j9...
console.log(webhook.secret); // whsec_... store this securely!

The secret is returned only once in the creation response. Store it in a secure environment variable immediately — it cannot be retrieved again, but you can rotate it via PUT /webhooks/{id}.

Event Types

Subscribe to specific events by passing their identifiers in the events array. If you omit the array, AgentSend delivers all event types to your endpoint.

Event Description
domain.verified A custom domain has passed DNS verification and is ready to use.
message.received An inbound email has arrived in one of your agent's inboxes.
message.sent An outbound message has been accepted and queued for delivery.
message.delivered The recipient's mail server confirmed successful delivery.
message.bounced The message could not be delivered (hard or soft bounce).
message.complained The recipient marked the message as spam via their mail client.

Webhook Payload

Every webhook delivery is a POST request with a Content-Type: application/json body. The payload has a consistent envelope regardless of event type.

json — message.received example
{
  "id": "evt_01j9xkq7z8abc",
  "type": "message.received",
  "createdAt": "2026-04-16T10:23:45.000Z",
  "webhookId": "wh_01j9abc123",
  "data": {
    "id": "msg_01j9xkp4n5def",
    "inboxId": "inbox_01j8mq3r2t",
    "threadId": "thr_01j9xkp4m1ghi",
    "fromAddress": "alice@example.com",
    "fromName": "Alice Chen",
    "toAddresses": ["a1b2c3@agentsend.io"],
    "subject": "Re: Your proposal",
    "bodyText": "Looks great, let's move forward.",
    "bodyHtml": "<p>Looks great, let's move forward.</p>",
    "hasAttachments": false,
    "receivedAt": "2026-04-16T10:23:44.812Z"
  }
}

Verifying Signatures

Every webhook request includes an X-AgentSend-Signature header. This is an HMAC-SHA256 hash of the raw request body, signed with your webhook's secret. Always verify this signature before processing a delivery to protect against forged requests.

javascript — Express / Node.js
import crypto from "node:crypto";

app.post("/hooks/agentsend", express.raw({ type: "application/json" }), (req, res) => {
  const sig       = req.headers["x-agentsend-signature"];
  const secret    = process.env.AGENTSEND_WEBHOOK_SECRET;
  const expected  = crypto
    .createHmac("sha256", secret)
    .update(req.body) // raw Buffer — do NOT parse JSON first
    .digest("hex");

  if (!crypto.timingSafeEqual(
    Buffer.from(sig),
    Buffer.from(expected)
  )) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body);
  // handle event.type …
  res.sendStatus(200);
});
python — Flask
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)

@app.route("/hooks/agentsend", methods=["POST"])
def webhook():
    sig      = request.headers.get("X-AgentSend-Signature", "")
    secret   = os.environ["AGENTSEND_WEBHOOK_SECRET"].encode()
    expected = hmac.new(secret, request.data, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(sig, expected):
        abort(401)

    event = request.get_json()
    # handle event["type"] …
    return "", 200
💡

Always use a constant-time comparison (e.g. crypto.timingSafeEqual or hmac.compare_digest) when checking signatures to prevent timing attacks.

Retry Policy

AgentSend considers a delivery successful when your endpoint responds with any 2xx HTTP status code within 10 seconds. If the request times out or returns a non-2xx response, the delivery is retried with exponential back-off:

Attempt Delay after previous attempt
1st retry 5 seconds
2nd retry 30 seconds
3rd retry 5 minutes
4th retry 30 minutes
5th retry 2 hours

After 5 failed retries (6 total attempts) the delivery is marked as failed and no further attempts are made. You can inspect failed deliveries in the delivery logs and manually replay them from the dashboard.

Respond with 200 OK as quickly as possible. If your processing logic takes time, accept the payload immediately and handle it asynchronously with a queue or background job.

Managing Webhooks

The webhooks API lets you list, update, and delete your registered endpoints.

List webhooks

Returns all webhooks registered on your account.

bash
# GET /webhooks
curl https://api.agentsend.io/webhooks \
  -H "x-api-key: $AGENTSEND_API_KEY"

Update a webhook

Change the URL, description, or subscribed event types. You can also rotate the signing secret by passing "rotateSecret": true — the response will include a new secret.

bash
# PUT /webhooks/{id}
curl -X PUT https://api.agentsend.io/webhooks/wh_01j9abc123 \
  -H "x-api-key: $AGENTSEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events": ["message.received", "message.delivered", "message.bounced"]}'

Delete a webhook

Removes the webhook permanently. In-flight deliveries are still attempted before the deletion takes effect.

bash
# DELETE /webhooks/{id}
curl -X DELETE https://api.agentsend.io/webhooks/wh_01j9abc123 \
  -H "x-api-key: $AGENTSEND_API_KEY"

Activity monitoring

Get a summary of recent event volume and error rates for a specific webhook.

bash
# GET /webhooks/activity
curl "https://api.agentsend.io/webhooks/activity?webhookId=wh_01j9abc123" \
  -H "x-api-key: $AGENTSEND_API_KEY"

Delivery Logs

Every delivery attempt is recorded. Query the logs endpoint to inspect the full history for a webhook, including HTTP response codes, response bodies, and timing.

GET /webhooks/logs
Query Parameter Type Description
webhookId string Filter logs to a specific webhook.
status string success, failed, or pending.
limit number Number of records to return (default 50, max 200).
before string Cursor for pagination; pass the id of the last record seen.
bash
# Fetch failed deliveries for a webhook
curl "https://api.agentsend.io/webhooks/logs?webhookId=wh_01j9abc123&status=failed" \
  -H "x-api-key: $AGENTSEND_API_KEY"

Each log entry includes: