Webhooks require a Business tier subscription.
Overview
Borough uses the Standard Webhooks specification for signing webhook payloads. The SDK provides middleware helpers for Express and Next.js to verify signatures and parse events.Installation
The webhook helpers require thestandardwebhooks peer dependency:
Express
The SDK typesevent.type as string and event.data as unknown, so the examples cast data to the payload shape.
Next.js (App Router)
400 response, which Borough treats as a failed delivery and retries. Make handlers idempotent.
Event types
A listing going off-market always sends
listing.expired, never listing.status_changed.
Event payload
All events share the same shape:timestamp is when Borough detected the change, in UTC, but it has no Z or offset. Parse it as UTC: new Date(timestamp.replace(" ", "T") + "Z"). Passing it to new Date() unchanged reads it as local time.
Delivery timing
Events arrive after Borough detects the change, not when it happened:- Borough checks for new changes to deliver every 5 minutes and handles up to 100 changes per run across all subscribers, so a large batch (for example, many listings expiring at once) can take hours to drain.
- New, expired, and relisted listings are detected as Borough’s regular listing updates run: rentals about every 6 hours, sales about every 8 hours.
- Price changes on listings whose full detail Borough already holds are detected only when that detail is refreshed: in Borough’s periodic re-check, or when a paid customer’s detail request triggers a refresh. Those events can arrive days or weeks after the change. On-demand refreshes draw on a shared live-fetch budget, so while that budget is held for Borough’s scheduled updates, or live fetching is paused, these price events wait for the periodic re-check.
Manual verification
If you’re not using Express or Next.js, verify the signature manually:Webhook secret
Your webhook secret is returned only when you create a subscription.GET /v1/webhooks (borough.webhooks.list()) does not return it, so store it at creation time. It starts with whsec_ and should be stored securely (e.g., environment variable). Each subscription has its own unique secret.
Retry behavior
Failed deliveries (non-2xx responses) are retried up to 7 times with increasing delays. Ensure your handler returns a 2xx status within 10 seconds to acknowledge receipt. After all retries are exhausted, the delivery is moved to a dead letter queue and cannot be replayed through the API. Delivery is at-least-once, so the same event can arrive more than once. Deduplicate on thewebhook-id header, which stays the same for every attempt at delivering one event to one subscription.
If your plan drops below Business, Borough deactivates your subscriptions. Upgrading again does not turn them back on: re-enable each one with a PATCH setting active: true.