Webhooks: event catalog
Every lead event kind, the event envelope, the objects inside it, and example payloads.
Events are sent for known leads only, from the time your brokerage was enabled. Email open and click events cover emails sent in the previous three days; later opens still count in the statistics. Anonymous visitors produce no events; their views show up in listing totals.
| Kind | Fires when | data |
|---|---|---|
lead.created |
A new lead appears: a registration or a contact synced from your CRM | lead, leadType, createdBy |
lead.property_view |
A lead opens a listing page (once per lead, listing and day, with viewsToday) |
lead, property, viewsToday |
lead.favorite |
A lead saves a listing | lead, property |
lead.saved_search_created |
A saved search is created for the lead, by the lead or an agent | lead, savedSearch, createdBy |
lead.saved_search_updated |
A lead's saved search changes | lead, savedSearch, updatedBy |
lead.search |
A lead runs a listings or rentals search | lead, search.type |
lead.email_open |
First human open of a Ruuster email | lead, email |
lead.email_click |
First human click in a Ruuster email | lead, email |
lead.tour_request |
A lead requests a tour | lead, property, tourDate, partOfDay |
lead.contact_agent |
A lead sends the contact-agent form | lead, property (if sent from a listing), agentId |
lead.login |
A lead signs in | lead, platform (web, ios, android when known) |
lead.share |
A lead shares a listing | lead, property, channel |
lead.email_status_changed |
Ruuster's verdict on the lead's email address changes: a check or a send fails, the lead unsubscribes, delivery is turned back on, or a new lead's address gets its first verdict | lead, emailStatus |
webhook.test |
You call the test endpoint | message, subscriptionId |
Subscribe to kinds one by one, or to lead.* for all of them, including kinds added later.
lead.search and lead.property_view are high volume.
Envelope
| Field | Description |
|---|---|
id |
Event id, evt_…. De-duplicate on this |
kind |
One of the kinds above |
occurredAt |
When it happened in Ruuster |
brokerageId |
Whose event it is |
apiVersion |
v1 |
data |
Kind-specific, see above |
links |
API URLs for the lead and listing involved |
Objects inside data
| Object | Fields |
|---|---|
lead |
leadId, firstName, lastName, crmExternalId, agentId; email and phone when lead contact details are on |
property |
propertyId, mlsRecordId, mlsName, address, ownedByYou, for any listing a lead engaged with, yours (ownedByYou: true) or another brokerage's (false). links.property points at the listing's engagement for yours and at Get a listing otherwise. A listing that has since left the feed carries propertyId and ownedByYou only |
savedSearch |
savedSearchId, name |
email |
emailId, subject, savedSearchName, sentAt |
createdBy, updatedBy |
{ "type": "lead" | "agent", "id": … } |
emailStatus |
status (valid, invalid, blocked, soft_bounce, complaint, unsubscribed, unknown), code, reason, source, failedAt, since, previousStatus. See the Email status guide for what each status means |
Example deliveries
Every delivery is a POST with these headers, then one of the bodies below:
POST /ruuster HTTP/1.1
Host: hooks.saguarorealty.com
Content-Type: application/json
User-Agent: Ruuster-Webhooks/1.0
Ruuster-Event-Id: evt_9a17f2c4b8e05d31
Ruuster-Event-Kind: lead.tour_request
Ruuster-Delivery-Id: dlv_4d7b2e90c1
Ruuster-Signature: t=1790869330,v1=6b0f3c2e8a91d4f7b5c0e2a9d8f61b3c7e4a2d0f9b8c6e1a3d5f7b9c2e4a6d8f
Common envelope (shown in full once; the examples after it show only kind, occurredAt and data):
{
"id": "evt_9a17f2c4b8e05d31",
"kind": "lead.tour_request",
"occurredAt": "2026-09-30T02:19:03Z",
"brokerageId": "4f6c2a9e-8d31-4b57-9a0e-2c7d5e1b8f43",
"apiVersion": "v1",
"data": {
"lead": { "leadId": "user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04", "firstName": "Sam", "lastName": "Lee",
"email": "sam.lee@example.com", "phone": "+15205550142", "crmExternalId": "48213",
"agentId": "7c1e9b42-3d85-4a6f-9e07-1b2c8d4f5a69" },
"property": { "propertyId": "5b7e2d94-1c63-4a8f-b5e0-9d3c7a2f1e86", "mlsRecordId": "22418765",
"address": "4182 E Saguaro Bloom Dr, Tucson, AZ 85718", "ownedByYou": true },
"tourDate": "2026-10-04",
"partOfDay": "morning"
},
"links": {
"lead": "https://api.ruuster.com/v1/leads/user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04/activity",
"property": "https://api.ruuster.com/v1/properties/5b7e2d94-1c63-4a8f-b5e0-9d3c7a2f1e86/engagement"
}
}
Below, LEAD_SAM stands for the lead object above, LEAD_PRIYA for
{ "leadId": "unregistered:8d2b6f19-4e37-4a85-b0c9-7f1e3d5a2b68", "firstName": "Priya", "lastName": "Shah", "email": "priya.shah@example.com", "phone": "+15205550187", "crmExternalId": "51907", "agentId": "e5a7d3c1-9b46-4f28-8c0e-6d2a1f7b3e95" },
and PROP_A for the property object above. In real deliveries these are always written out in full.
Lead events
{ "kind": "lead.created", "occurredAt": "2026-09-23T15:44:12Z",
"data": { "lead": LEAD_PRIYA, "leadType": "unregistered", "createdBy": { "type": "agent", "id": "e5a7d3c1-9b46-4f28-8c0e-6d2a1f7b3e95" } } }
{ "kind": "lead.property_view", "occurredAt": "2026-09-27T23:40:12Z",
"data": { "lead": LEAD_SAM, "property": PROP_A, "viewsToday": 3 } }
{ "kind": "lead.favorite", "occurredAt": "2026-09-29T14:21:55Z",
"data": { "lead": LEAD_SAM, "property": PROP_A } }
{ "kind": "lead.saved_search_created", "occurredAt": "2026-09-29T14:30:02Z",
"data": { "lead": LEAD_SAM, "savedSearch": { "savedSearchId": "b81f4d20-6c3e-4a97-9e15-0d7c2a5f8b36", "name": "Catalina Foothills 3bd" },
"createdBy": { "type": "lead", "id": "user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04" } } }
{ "kind": "lead.saved_search_updated", "occurredAt": "2026-09-30T16:05:41Z",
"data": { "lead": LEAD_SAM, "savedSearch": { "savedSearchId": "b81f4d20-6c3e-4a97-9e15-0d7c2a5f8b36", "name": "Catalina Foothills 3bd" },
"updatedBy": { "type": "agent", "id": "7c1e9b42-3d85-4a6f-9e07-1b2c8d4f5a69" } } }
{ "kind": "lead.search", "occurredAt": "2026-09-28T13:04:27Z",
"data": { "lead": LEAD_SAM, "search": { "type": "listings" } } }
{ "kind": "lead.email_open", "occurredAt": "2026-09-29T14:12:02Z",
"data": { "lead": LEAD_SAM,
"email": { "emailId": "em_7Yq2pX8mT4cW", "subject": "3 new homes match \"Catalina Foothills 3bd\"", "savedSearchName": "Catalina Foothills 3bd", "sentAt": "2026-09-29T12:00:31Z" } } }
{ "kind": "lead.email_click", "occurredAt": "2026-09-29T14:15:40Z",
"data": { "lead": LEAD_SAM,
"email": { "emailId": "em_7Yq2pX8mT4cW", "subject": "3 new homes match \"Catalina Foothills 3bd\"", "savedSearchName": "Catalina Foothills 3bd", "sentAt": "2026-09-29T12:00:31Z" } } }
{ "kind": "lead.tour_request", "occurredAt": "2026-09-30T02:19:03Z",
"data": { "lead": LEAD_SAM, "property": PROP_A, "tourDate": "2026-10-04", "partOfDay": "morning" } }
{ "kind": "lead.contact_agent", "occurredAt": "2026-09-30T19:07:56Z",
"data": { "lead": LEAD_PRIYA,
"property": { "propertyId": "a03c8f61-7e2b-4d95-8a14-6f0b3e9c2d57", "mlsRecordId": "22419302", "address": "1920 W Mesquite Ridge Ln, Oro Valley, AZ 85737", "ownedByYou": true },
"agentId": "e5a7d3c1-9b46-4f28-8c0e-6d2a1f7b3e95" } }
{ "kind": "lead.login", "occurredAt": "2026-09-30T02:11:40Z",
"data": { "lead": LEAD_SAM, "platform": "ios" } }
{ "kind": "lead.share", "occurredAt": "2026-09-30T23:58:44Z",
"data": { "lead": LEAD_SAM, "property": PROP_A, "channel": "sms" } }
{ "kind": "lead.email_status_changed", "occurredAt": "2026-10-29T14:10:00Z",
"data": { "lead": LEAD_SAM,
"emailStatus": { "status": "invalid", "code": "5.1.1 bounce",
"reason": "The mailbox does not exist or no longer accepts mail.",
"source": "delivery", "failedAt": "2026-10-29T14:02:11Z",
"since": "2026-10-29T14:10:00Z", "previousStatus": "valid" } } }
Test event
{ "id": "evt_test_1b6d0c93", "kind": "webhook.test", "occurredAt": "2026-10-01T14:21:30Z",
"brokerageId": "4f6c2a9e-8d31-4b57-9a0e-2c7d5e1b8f43", "apiVersion": "v1",
"data": { "message": "Hello from Ruuster", "subscriptionId": "whk_2c7f9a4e1b" } }
Batch (sent when a subscription would receive more than one event per second):
{
"id": "bat_0e4c7a91d2",
"kind": "batch",
"apiVersion": "v1",
"count": 2,
"events": [
{ "id": "evt_71c2e9b04d3a8f65", "kind": "lead.property_view", "occurredAt": "2026-09-28T13:05:02Z", "brokerageId": "4f6c2a9e-8d31-4b57-9a0e-2c7d5e1b8f43", "apiVersion": "v1", "data": { "lead": LEAD_SAM, "property": PROP_A, "viewsToday": 1 } },
{ "id": "evt_b4d90f2a6e17c385", "kind": "lead.search", "occurredAt": "2026-09-28T13:05:03Z", "brokerageId": "4f6c2a9e-8d31-4b57-9a0e-2c7d5e1b8f43", "apiVersion": "v1", "data": { "lead": LEAD_SAM, "search": { "type": "listings" } } }
]
}
Your endpoint answers any 2xx within 10 seconds, for example 200 with body ok. The body is ignored.