Skip to content

Webhooks

Borough webhooks deliver real-time notifications when listings change. Webhooks use the Standard Webhooks specification for payload signing and verification.

EventDescription
listing.createdA new listing appeared in the search index
listing.price_decreasedListing price dropped by more than $25
listing.price_increasedListing price increased by more than $25
listing.status_changedListing status changed (e.g., ACTIVE to IN_CONTRACT)
listing.expiredListing went off-market
{
"type": "listing.price_decreased",
"data": {
"listingId": "4961849",
"oldValue": "5222",
"newValue": "4900"
},
"timestamp": "2026-02-15T14:30:00Z"
}

Every webhook request includes three headers:

HeaderDescription
webhook-idUnique delivery ID
webhook-timestampUnix timestamp of signing
webhook-signaturev1,<base64-hmac-sha256>

The signature is computed as:

HMAC-SHA256(secret, "${webhook-id}.${webhook-timestamp}.${body}")
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);
})
);

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.

Use the Webhook CRUD endpoints to create, list, update, and delete subscriptions.