Skip to content

Webhook Payloads

This reference documents the structure and content of webhook payloads sent by the HouMeerOver system when calculation results are available.

Payload Structure

All webhook payloads contain complete calculation data in the following structure:

{
  "subsidy_amount": 1250.50,
  "calculated_at": "2024-01-15T10:35:42.789123Z",
  "created_at": "2024-01-15T10:30:00.123456Z",
  "modified_at": "2024-01-15T10:35:42.789123Z",
  "address": {
    "municipality": "0344",
    "postal_code": "1234AB", 
    "house_number": 42,
    "house_letter": "A",
    "house_number_addition": ""
  }
}

Payload Fields

Field Type Description
subsidy_amount number The calculated subsidy amount in euros
calculated_at string ISO 8601 timestamp when the calculation was completed by the calculation engine
created_at string ISO 8601 timestamp when the calculation was created
modified_at string ISO 8601 timestamp when the calculation was last modified
address object Address information for the calculation
address.municipality string Municipality code (CBS code)
address.postal_code string Dutch postal code (e.g., "1234AB")
address.house_number number House number
address.house_letter string House letter (can be empty)
address.house_number_addition string House number addition (can be empty)

When Webhooks are Sent

Webhooks are triggered when:

  • A calculation result is received from the external calculation engine via the calculation API
  • The municipality has an active webhook configuration
  • The webhook is enabled and has a valid URL configured

Example Calculation Result

{
  "subsidy_amount": 2450.75,
  "calculated_at": "2024-01-15T09:18:42.123456Z",
  "created_at": "2024-01-15T09:15:23.456789Z",
  "modified_at": "2024-01-15T09:18:42.123456Z",
  "address": {
    "municipality": "0344",
    "postal_code": "3911AB",
    "house_number": 123,
    "house_letter": "B",
    "house_number_addition": ""
  }
}

This payload indicates:

  • A subsidy of €2,450.75 was calculated
  • The calculation was created at 09:15 and completed at 09:18
  • The address is in municipality 0344 (Rhenen), postal code 3911AB, house number 123 with house letter B

Processing Webhook Payloads

Signature Verification

All webhook payloads are signed with HMAC-SHA256. The signature is included in the X-Webhook-Signature header:

import hmac
import hashlib
import json

def verify_webhook_signature(payload_body: bytes, signature: str, secret: str) -> bool:
    """Verify webhook signature matches expected signature."""
    # The payload is signed as JSON with sorted keys and minimal separators
    payload_dict = json.loads(payload_body)
    canonical_payload = json.dumps(payload_dict, sort_keys=True, separators=(',', ':'))

    expected_signature = hmac.new(
        secret.encode('utf-8'),
        canonical_payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(f"sha256={expected_signature}", signature)

# Usage
webhook_signature = request.headers.get('X-Webhook-Signature')
if verify_webhook_signature(request.data, webhook_signature, WEBHOOK_SECRET):
    # Process webhook
    payload = request.get_json()
    process_calculation_result(payload)
else:
    # Invalid signature
    return 401

Error Handling

Return appropriate HTTP status codes:

  • 200-299: Success, webhook processed
  • 400-499: Client error, will not retry
  • 500-599: Server error, will retry according to retry policy
@app.route('/webhooks/houmeeover', methods=['POST'])
def handle_webhook():
    try:
        # Verify signature first
        signature = request.headers.get('X-Webhook-Signature')
        if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
            return jsonify({"error": "Invalid signature"}), 401

        # Process calculation result
        payload = request.get_json()
        process_calculation_result(payload)
        return jsonify({"status": "success"}), 200

    except ValueError as e:
        # Client error - don't retry
        logger.error(f"Invalid calculation data: {str(e)}")
        return jsonify({"error": "Invalid data"}), 400
    except Exception as e:
        # Server error - will retry
        logger.error(f"Webhook processing error: {str(e)}")
        return jsonify({"error": "Internal server error"}), 500

def process_calculation_result(payload):
    """Process the calculation result webhook payload."""
    subsidy_amount = payload['subsidy_amount']
    address = payload['address']

    # Update your local records
    update_calculation_result(
        municipality=address['municipality'],
        postal_code=address['postal_code'],
        house_number=address['house_number'],
        house_letter=address['house_letter'],
        house_number_addition=address['house_number_addition'],
        subsidy_amount=subsidy_amount,
        calculated_at=payload['calculated_at']
    )

Testing Webhooks

You can test webhook delivery from the HouMeerOver admin interface. A test webhook will be sent with sample calculation data.

Next Steps


This documentation is part of the HouMeerOver Third Party Integration integration guide.