Overview
Setting up webhooks involves two main steps: implementing a webhook endpoint on your server and registering it with Modulus Labs. This guide walks you through the complete setup process.1
Implement Webhook Endpoint
Create a POST endpoint on your server that can receive and process webhook notifications
2
Register with Modulus Labs
Use the Create Webhook API to register your endpoint URL
3
Test Your Integration
Use the Simulate API to verify your webhook handler works correctly
4
Go Live
Enable your webhook and start receiving real transaction notifications
Prerequisites
Before registering a webhook, ensure you have:Publicly Accessible Endpoint
Your webhook URL must be accessible from the internet (not localhost)
HTTPS Support
Webhook URLs must use HTTPS for security (HTTP not supported in production)
Encryption Key
For decrypting the webhook JWE. Your Secret Key is separate - it authenticates your management-API calls.
Fast Response Time
Endpoint must respond promptly to avoid timeouts
Step 1: Implement Your Webhook Endpoint
Your server needs onePOST endpoint that accepts Content-Type: application/json, decrypts the delivery, and returns 200 or 201 quickly.
The delivery body is { "Token": "<JWE>" }. Decrypt the Token with your Encryption Key (not the Secret Key), deduplicate on (transactionId, webhookAction), acknowledge, then process off the request path.
Receiving Webhooks has the complete, copy-ready handler, and Webhook Payload documents every field you get back. Build your endpoint from those, then come back here to register it.
Decrypt with the Encryption Key, correlate to your order on
merchantReferenceNumber, and return 200 or 201 only after you have durably stored the event - failed deliveries are retried up to 3 more times, about 15 minutes apart, then stop (4 attempts total).Step 2: Register Your Webhook
The webhook management API is encrypted end to end. You send an encrypted envelope and get one back:Token by encrypting your JSON body as a JWE with your Encryption Key (alg A256KW, enc A256CBC-HS512) - the same key and code you use in Encryption & JWE Tokens. Decrypt the response.Token the same way. Authenticate the call itself with HTTP Basic Auth using your Secret Key.
A webhook registers one action. To receive both success and declined events, register twice.
Body you encrypt (create):
response.Token:
id to update or delete the webhook later. See Create Webhook for the full field list.
webhookAction is singular - one webhook, one action (QRPH_SUCCESS or QRPH_DECLINED). There is no actions array and no status field on create; new webhooks are enabled.Step 3: Test Your Webhook
Use the Simulate API to send a real encrypted webhook to your endpoint without a live transaction. It takes the same encrypted envelope. The body you encrypt is:useCase is one of SUCCESS, MISSING_DESTINATION_ACCOUNT, MISSING_PARTNER_REVENUE_ACCOUNT, or UNSUPPORTED_TRANSFER_TYPE. The three failure cases drive a QRPH_DECLINED delivery.
Managing Webhooks
All management calls use the same encrypted envelope and Basic Auth.- Update - encrypt
{ "callbackUrl": "...", "webhookStatus": "ENABLED" }(both required;webhookStatusisENABLEDorDISABLED). Update cannot change the action - delete and re-create to change it. Disable instead of deleting during maintenance. - List -
GET /v1/webhooksreturns your webhooks in an encryptedresponse.Token. - Delete -
DELETE /v1/webhooks/{id}permanently removes one.
Multi-Merchant Support
Each delivery carries anActivation-Code header identifying which sub-merchant account the event belongs to. Route on it:
Best Practices
Use HTTPS
Register only HTTPS callback URLs. HTTP is not supported in production.
Separate URLs per environment
Use different callback URLs for sandbox and production so test and live data never mix.
Deduplicate every delivery
Retries resend the same body. Key on
(transactionId, webhookAction) before acting.Acknowledge with 200 or 201
Only
200 and 201 stop retries. Return one after you durably store the event; log business-logic failures and retry them yourself.Troubleshooting
Webhook Not Receiving Notifications
Webhook Not Receiving Notifications
Possible causes:
- Callback URL is not publicly accessible
- Firewall blocking incoming requests
- Webhook status is DISABLED
- TLS certificate issues
- Test URL accessibility from an external network
- Verify status is
ENABLEDviaGET /v1/webhooks - Check server logs for incoming requests
- Use the Simulate API to send a test webhook
Decryption Failures
Decryption Failures
Possible causes:
- Decrypting with the Secret Key instead of the Encryption Key
- JWE library not configured correctly
- Reading the wrong body field (the JWE is in
Token)
- Verify you are using your Encryption Key, the same key used elsewhere in the QR Ph API
- Log the raw
Tokenfor inspection - Test decryption with the Simulate API first
- Check JWE library documentation for your language
Duplicate Webhooks
Duplicate Webhooks
Possible causes:
- Retries (expected when you did not return
200/201) - Slow endpoint timing out before acknowledging
- Deduplicate on
(transactionId, webhookAction)before processing - Persist processed keys with a unique constraint to survive races
- Acknowledge fast, then process asynchronously
Security Checklist
1
Transport
- Callback URL uses HTTPS with a valid certificate
2
Authenticity
- Every delivery decrypts with your Encryption Key (a body you cannot decrypt is not from Modulus)
- Correlate to your order on
merchantReferenceNumber
3
Idempotency
- Deduplicate on
(transactionId, webhookAction) - Reject or ignore an event you have already processed
4
Endpoint hygiene
- Errors do not leak system internals
- Decryption and processing failures are logged and alerted
Next Steps
Receiving Webhooks
Decrypt, acknowledge, and process
Webhook Payload
Every field in the decrypted payload
Create Webhook API
Full create/update/delete reference
Simulate API
Test with SUCCESS and the failure cases