Webhook events
agentinbox.pro delivers inbound mail as JSON POST requests to your configured webhookUrl. This is the primary consumption path — the events API is for inspection and debugging.
Request headers
Section titled “Request headers”POST https://your-agent.example.com/webhooks/inbound-emailContent-Type: application/jsonX-AgentMail-Event-Id: 05df8afb-b4bd-4f4c-87e9-334594b9acccX-AgentMail-Timestamp: 1718246400X-AgentMail-Signature: t=1718246400,v1=<hmac_hex>Payload
Section titled “Payload”{ "event": "inbound.email.received", "event_id": "05df8afb-b4bd-4f4c-87e9-334594b9accc", "inbound_email_id": "05df8afb-b4bd-4f4c-87e9-334594b9accc", "idempotency_key": "a1b2c3...", "message_id": "<vendor-123@example.com>", "domain": "acme-agent.com", "recipient": "billing@acme-agent.com", "from": "vendor@example.com", "subject": "Invoice #123", "text": "Attached is your invoice.", "html": null, "attachments": [], "receivedAt": "2026-06-13T01:20:00.000Z", "rawRef": "inbound/<domain-id>/<uuid>.eml"}Field reference
Section titled “Field reference”| Field | Description |
|---|---|
event | Always inbound.email.received today |
event_id | Stable identifier — use for deduplication |
idempotency_key | Hash of domain + message_id |
message_id | Parsed RFC Message-ID header, if present |
domain | Verified recipient domain |
recipient | Full SMTP address — route agents on this |
from | Sender address |
subject | Email subject |
text / html | Parsed body |
attachments | Metadata only (filename, contentType, size) |
rawRef | Pointer to stored raw .eml |
Your response
Section titled “Your response”Return any HTTP 2xx:
{ "ok": true }Non-2xx responses are recorded as failed delivery. agentinbox.pro retries with backoff; persistent failures become dead letters.
Signature verification
Section titled “Signature verification”Sign secret: workspace webhook_signing_secret (whsec_...) from POST /api/workspaces.
HMAC-SHA256(secret, "{timestamp}.{raw_body}")Header: X-AgentMail-Signature: t=<timestamp>,v1=<hex>Reject when:
- timestamp is outside ±5 minutes
- HMAC does not match
X-AgentMail-Event-Id≠ payloadevent_id
Node.js
Section titled “Node.js”import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyAgentMailWebhook(rawBody, headers, secret) { const timestamp = headers["x-agentmail-timestamp"]; const signature = headers["x-agentmail-signature"]; if (!timestamp || !signature) return false;
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)); if (age > 300) return false;
const expected = createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex");
const match = signature.match(/^t=\d+,v1=([a-f0-9]+)$/); if (!match) return false;
return timingSafeEqual(Buffer.from(match[1]), Buffer.from(expected));}Python
Section titled “Python”import hmacimport hashlibimport reimport time
def verify_agentmail_webhook(raw_body: bytes, headers: dict, secret: str) -> bool: timestamp = headers.get("x-agentmail-timestamp") signature = headers.get("x-agentmail-signature") if not timestamp or not signature: return False if abs(int(time.time()) - int(timestamp)) > 300: return False expected = hmac.new( secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256, ).hexdigest() match = re.match(r"^t=\d+,v1=([a-f0-9]+)$", signature) return bool(match and hmac.compare_digest(match.group(1), expected))Recommended handler flow
Section titled “Recommended handler flow”1. Read raw body (before JSON parse) for signature check2. Verify HMAC + timestamp + event id3. Dedupe on event_id or idempotency_key4. Route by recipient address5. Return 2xx only after the event is safely acceptedDelivery audit
Section titled “Delivery audit”GET /api/domains/{id}/delivery-attemptsAuthorization: Bearer <api_key>Replay a failed event manually:
POST /api/domains/{id}/events/{eventId}/replayAuthorization: Bearer <api_key>