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.