Webhooks
Webhooks send real-time EasyAgentIDX activity to your server. They are available on Pro and higher plans and use encrypted signing secrets, public HTTPS destinations, and automatic retry delivery.
Create an endpoint
- Open Developer in the customer dashboard.
- Select Webhooks and then Add Webhook.
- Enter a public HTTPS endpoint and choose the events to receive.
- Copy the signing secret immediately. It is shown only once.
Warning
Store the signing secret in a server environment variable. Never place it in browser code, a mobile bundle, version control, logs, or an AI prompt.Events
| Event | Sent when |
|---|---|
lead.created | A widget, registration flow, or Developer API request creates a lead |
lead.updated | A dashboard user or Developer API request updates a lead |
valuation.created | A homeowner or Developer API request completes a home valuation |
visitor.registered | A visitor creates an account through an approved widget |
search.saved | An authenticated visitor saves a search |
widget.created | The public API creates a widget |
widget.updated | The public API changes a widget |
widget.deleted | The public API removes a widget |
Widget events contain id and the issuing key's public site_id. They do not contain configuration, lead rules, or secrets. These events are queued in the widget transaction and delivered by the webhook worker. Normal first delivery is within the next scheduled worker run. Retried widget requests do not enqueue the event again.
New widget events include schema_version: "1", a stable event_id, and the original occurred_at timestamp. The event ID is shared across subscribed destinations. Each destination has its own stable delivery ID. These identifiers and the occurrence time stay unchanged across retries. The signed delivery timestamp is refreshed on each attempt. Existing lead and valuation event envelopes are unchanged.
Request headers
| Header | Purpose |
|---|---|
X-EasyAgentIDX-Signature | sha256= followed by the HMAC digest |
X-EasyAgentIDX-Timestamp | ISO timestamp included in the signed message |
X-EasyAgentIDX-Delivery | Stable delivery ID used across retries |
X-EasyAgentIDX-Event | Event name |
Payload
{
"deliveryId": "b6e7b3df-53ba-4bbc-a657-8a0eb8b7fa54",
"event": "lead.created",
"timestamp": "2026-08-25T18:30:00.000Z",
"data": {
"id": "cm...",
"name": "Jane Smith",
"email": "jane@example.com"
}
}Verify every request
Reject stale timestamps, calculate the HMAC over timestamp.rawBody, and compare the complete sha256= value with a timing-safe function. Parse JSON only after verification.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyEasyAgentIDXWebhook(
rawBody: string,
signature: string,
timestamp: string,
secret: string
) {
const age = Math.abs(Date.now() - Date.parse(timestamp));
if (!Number.isFinite(age) || age > 5 * 60 * 1000) return false;
const expected = "sha256=" + createHmac("sha256", secret)
.update(timestamp + "." + rawBody)
.digest("hex");
const receivedBytes = Buffer.from(signature);
const expectedBytes = Buffer.from(expected);
return receivedBytes.length === expectedBytes.length &&
timingSafeEqual(receivedBytes, expectedBytes);
}Retries and idempotency
Return a 2xx response within 10 seconds. Failed deliveries are retried three times after approximately 1 minute, 5 minutes, and 15 minutes. Store processed X-EasyAgentIDX-Delivery values so a retry cannot create duplicate work.
Destination security
Endpoints must use HTTPS on port 443. Localhost, private networks, cloud metadata addresses, URL credentials, oversized responses, and redirects are rejected. DNS is resolved and pinned to the validated public address for each delivery attempt.