Webhooks require a Business tier subscription.
Overview
Borough webhooks deliver real-time notifications when listings change. Webhooks use the Standard Webhooks specification for payload signing and verification.
Event types
| Event | Description |
|---|
listing.created | A new listing appeared in the search index |
listing.price_decreased | Listing price dropped by more than $25 |
listing.price_increased | Listing price increased by more than $25 |
listing.status_changed | Listing status changed (e.g., ACTIVE to IN_CONTRACT) |
listing.expired | Listing went off-market |
{
"type": "listing.price_decreased",
"data": {
"listingId": "4961849",
"oldValue": "5222",
"newValue": "4900"
},
"timestamp": "2026-02-15T14:30:00Z"
}
Signature verification
Every webhook request includes three headers:
| Header | Description |
|---|
webhook-id | Unique delivery ID |
webhook-timestamp | Unix timestamp of signing |
webhook-signature | v1,<base64-hmac-sha256> |
The signature is computed as:
HMAC-SHA256(secret, "${webhook-id}.${webhook-timestamp}.${body}")
Verify with the SDK
import { webhookMiddleware } from '@borough/sdk/webhooks/express';
app.post('/webhooks/borough',
express.raw({ type: 'application/json' }),
webhookMiddleware(process.env.BOROUGH_WEBHOOK_SECRET, async (event) => {
console.log('Received:', event.type, event.data);
})
);
Retry schedule
Failed deliveries (non-2xx responses) are retried up to 7 times with increasing delays. After all retries are exhausted, the delivery is moved to a dead letter queue.
Managing subscriptions
Use the Webhook CRUD endpoints to create, list, update, and delete subscriptions.