Let me provide a comprehensive solution addressing all three aspects of webhook reliability:
Webhook Idempotency:
The core issue is that eventId alone isn’t sufficient for idempotency. Implement proper deduplication in your webhook handler:
- Create a webhook events table:
CREATE TABLE webhook_events (
event_id VARCHAR(100) PRIMARY KEY,
contract_id VARCHAR(50),
event_type VARCHAR(50),
processed_at TIMESTAMP,
business_key VARCHAR(200)
);
- Use composite business key for deduplication:
business_key = f"{contract_id}_{event_type}_{renewal_date}"
- Check before processing:
if webhook_events.exists(business_key):
return 200 # Already processed
The eventId is a delivery identifier, not a business event identifier. Multiple deliveries of the same business event will have different eventIds, so you must deduplicate using business context (contract + event + date).
Event Payload Design:
Request enhanced payload from Infor OS to include proper deduplication metadata:
- Modify webhook subscription to include extended payload:
- Go to Infor OS Portal > Integration > Webhooks
- Edit subscription: “Subscription Renewal Events”
- Enable “Extended Payload” option
- Add custom fields:
{
"eventId": "{{event.id}}",
"eventTimestamp": "{{event.timestamp}}",
"contractId": "{{contract.id}}",
"eventType": "{{event.type}}",
"renewalDate": "{{contract.renewal_date}}",
"renewalAmount": "{{contract.renewal_amount}}",
"contractVersion": "{{contract.version}}"
}
The contractVersion field is crucial - it changes only when the contract actually updates, providing a reliable deduplication signal.
- Use this enhanced payload for idempotency:
business_key = f"{contractId}_{renewalDate}_{contractVersion}"
Contract Renewal Workflow:
Configure the workflow to prevent multiple webhook triggers:
-
In CloudSuite, navigate to Subscription Management > Workflow Configuration
-
Edit “Contract Renewal Workflow”
-
Review webhook trigger points:
- Renewal Initiated (intermediate state)
- Renewal Approved (intermediate state)
- Renewal Completed (final state) ✓
- Renewal Activated (post-completion state)
-
Configure webhook to fire ONLY on “Renewal Completed” state
-
Set webhook event filter:
{
"event_type": "subscription_renewed",
"filter": {
"status": "COMPLETED",
"exclude_intermediate_states": true
}
}
Additional Safeguards:
- Implement webhook response timeout:
@app.route('/webhook/subscription', methods=['POST'])
def handle_subscription_webhook():
# Acknowledge immediately
response = jsonify({"status": "received"})
# Process asynchronously
queue.enqueue(process_renewal, request.json)
return response, 200
Responding within 2-3 seconds prevents retry logic from triggering.
- Add webhook signature verification:
def verify_webhook_signature(payload, signature):
expected = hmac.new(webhook_secret, payload, sha256).hexdigest()
return hmac.compare_digest(expected, signature)
- Implement exponential backoff for processing:
if is_duplicate(business_key):
logger.info(f"Duplicate webhook ignored: {business_key}")
return 200 # Still return success to prevent retries
Monitoring and Alerting:
Set up monitoring to detect duplicate webhook issues:
- Track duplicate rate:
SELECT business_key, COUNT(*) as duplicates
FROM webhook_events
WHERE created_at > NOW() - INTERVAL '1 day'
GROUP BY business_key
HAVING COUNT(*) > 1;
-
Alert if duplicate rate exceeds threshold (>5% is concerning)
-
Log all webhook deliveries with eventId and business_key for debugging
Testing Idempotency:
Verify your implementation handles duplicates correctly:
- Send test webhook twice with same business context
- Verify only one invoice is created
- Check that second webhook returns 200 but doesn’t process
- Confirm webhook_events table shows single processed record
This comprehensive approach ensures reliable webhook processing with proper idempotency, preventing duplicate billing while maintaining compatibility with Infor OS webhook delivery guarantees.
This draft is based on general Infor CloudSuite knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.