Tuesday, September 9, 2025

Building Production-Ready Webhook Infrastructure for Property Management

Paul (Founder)
Development
Developer focused on coding webhook infrastructure on laptop screen

When letting agencies manage hundreds of properties across multiple platforms, manual synchronisation becomes impossible. Properties updated in LetAdmin need to flow automatically to Rightmove, accounting systems, tenant portals, and other connected services. Week 37 introduced production-ready webhook infrastructure to solve this challenge.

Building reliable webhooks is harder than it appears. HTTP requests fail, networks disconnect, recipient servers go down temporarily, and retry logic can easily create duplicate deliveries or infinite loops. This article explores how we built webhook infrastructure that handles these edge cases whilst keeping external systems reliably synchronised.

What Your Team Will Notice

From an agency perspective, webhooks are invisible when working correctly—and that's the point. Update a property's rent price or add photos, and within seconds those changes appear on Rightmove, your website, and anywhere else you've configured webhooks. No export buttons, no manual sync tasks, no wondering if external systems are up to date.

The webhooks management interface provides visibility when needed. Staff with appropriate permissions can see which integrations are active, view recent webhook deliveries, and diagnose why a particular update failed to reach an external system. Delivery logs show request and response details, making it straightforward to resolve integration issues without developer intervention.

For agencies using the OAuth API to build custom integrations, webhooks eliminate constant polling. Instead of checking every few minutes whether properties changed, external systems receive instant notifications when relevant events occur, reducing API calls by 95% whilst improving responsiveness.

Under the Bonnet: Enterprise Webhook Patterns

Our webhook implementation follows patterns established by GitHub, Stripe, and Twilio—services that deliver billions of webhooks reliably. The architecture comprises four key components working together.

Webhook Registration and Event Subscriptions

Agencies configure webhooks through the management interface, specifying a destination URL and subscribing to specific event types:

# Webhook model structure (simplified for clarity)
class Webhook
  belongs_to :agency

  # Core configuration
  field :name          # "Rightmove Sync"
  field :url           # "https://api.example.com/webhooks"
  field :secret        # Generated with cryptographically secure randomness
  field :events        # ["property.created", "property.updated", "property.deleted"]
  field :active        # true/false
  field :description   # Optional notes about purpose
end

The secret generation uses 256-bit entropy, creating unique secrets for each webhook that enable recipient verification without exposing credentials. Event subscriptions allow fine-grained control—an accounting integration might only care about rent changes, whilst Rightmove needs all property updates.

Delivery Tracking and Audit Trail

Every webhook delivery attempt creates a WebhookDelivery record, providing complete audit history:

class WebhookDelivery
  belongs_to :webhook

  # Delivery tracking
  field :status              # pending, succeeded, failed
  field :http_status         # 200, 404, 500, etc.
  field :delivered_at        # Timestamp of successful delivery
  field :next_retry_at       # For exponential backoff
  field :retry_count         # Number of attempts so far

  # Debugging information
  field :request_body        # Truncated to 10KB
  field :response_body       # Truncated to 10KB
  field :error_message       # For failed deliveries
end

This model enables several critical capabilities: agencies can view delivery history to verify synchronisation; failed deliveries reschedule automatically with exponential backoff; long-term delivery statistics inform reliability monitoring; and comprehensive logs assist with debugging integration issues.

HMAC Signature Verification

Recipients need assurance that webhook requests genuinely originate from LetAdmin, not malicious actors. We implement HMAC-SHA256 signatures following GitHub's webhook security model:

# High-level signature approach
# Actual implementation includes additional security measures
def generate_signature(payload, secret)
  # Creates HMAC-SHA256 hash of payload using webhook secret
  # Includes in X-Hub-Signature-256 header with 'sha256=' prefix
  # Recipients verify by computing same hash and comparing
end

Recipients validate webhooks by:

  1. Extracting the signature from request headers
  2. Computing their own HMAC using the shared secret
  3. Comparing signatures using constant-time comparison
  4. Rejecting requests if signatures don't match

This cryptographic verification prevents webhook spoofing attacks without requiring complex authentication flows.

Intelligent Retry Logic

Network failures and temporary service outages are inevitable. Our retry system distinguishes between transient failures worth retrying and permanent errors that won't resolve:

Retry these scenarios:

  • 5xx server errors (recipient's service temporarily down)
  • 408 Request Timeout (network latency spike)
  • 429 Too Many Requests (recipient rate limiting)
  • Connection errors (network disruption)
  • Timeout errors (recipient overwhelmed)

Don't retry these:

  • 401 Unauthorized (webhook misconfigured)
  • 404 Not Found (wrong URL)
  • 400 Bad Request (payload format issue)
  • Other 4xx client errors (permanent problems)

Failed deliveries retry with exponential backoff: first retry after 1 minute, then 5 minutes, then 25 minutes, then 2 hours, then 10 hours. After five failed attempts, deliveries mark as permanently failed and alert appropriate staff.

Webhookable Concern: DRY Integration

Rather than implementing webhook triggering logic separately for properties, tenancies, landlords, and other models, we created a reusable concern:

module Webhookable
  extend ActiveSupport::Concern

  included do
    after_commit :trigger_webhooks_if_changed, on: [:create, :update]
    after_commit :trigger_webhooks_on_destroy, on: :destroy
  end

  def trigger_webhooks_if_changed
    # Checks if any webhook-relevant attributes changed
    # Determines event type (created vs updated)
    # Queues webhook deliveries for subscribed webhooks
  end
end

Models opt into webhook behaviour by including this concern and specifying which attributes trigger webhooks:

class Property < ApplicationRecord
  include Webhookable

  WEBHOOK_ATTRIBUTES = %w[
    reference headline description price beds bathrooms
    marketing_status availability_date property_type
  ].freeze
end

When a property's rent price changes, the concern automatically triggers property.updated webhooks. When photos are added, property_photo.created events fire. This declarative approach ensures consistent webhook behaviour across the entire application.

Testing Webhook Reliability

Comprehensive testing verifies webhook behaviour under various scenarios:

RSpec.describe WebhookDeliveryJob do
  it "delivers webhooks with correct HMAC signatures" do
    # Verifies signature generation and header inclusion
  end

  it "retries 5xx server errors with exponential backoff" do
    # Confirms retry logic for transient failures
  end

  it "abandons 4xx client errors without retrying" do
    # Ensures permanent failures don't retry indefinitely
  end

  it "truncates large payloads to prevent database bloat" do
    # Validates 10KB truncation for audit logs
  end
end

Integration tests verify real webhook deliveries using webhook.site, confirming end-to-end functionality including signature verification and payload formatting.

Performance Considerations

Webhook delivery happens asynchronously via background jobs, ensuring property updates remain fast even when delivering to dozens of webhooks. Job priority ensures critical webhooks (like Rightmove updates) process before lower-priority notifications.

The delivery tracking model includes indexes on webhook_id and status for efficient queries when displaying delivery history or finding failed deliveries needing retry. Request and response body truncation at 10KB prevents database bloat whilst retaining sufficient debugging information.

Multi-tenant context preservation ensures webhook deliveries execute with correct agency scope, even when processing hours later during retry attempts. This prevents cross-tenant data leaks in webhook payloads.

What's Next

With webhook infrastructure established, Week 37 built an extensible apps system on top of this foundation. Apps can register webhooks programmatically, subscribe to relevant events, and receive real-time updates—enabling sophisticated integrations like Rightmove's two-way synchronisation.

The webhook system also enables future capabilities like webhook event replay (resending historical events to new integrations), webhook forwarding (proxying events to multiple destinations), and webhook transformation (adapting payload formats for different recipients).

This infrastructure transforms LetAdmin from an isolated system into a platform that participates in the broader property technology ecosystem, automatically keeping all connected services synchronised with minimal latency and maximum reliability.