Webhooks
Last updated:November 07, 2025
Webhooks are HTTP-based callbacks that notify your system when specific events occur on your entities. Instead of polling for updates, you configure a webhook once, and it pushes data to your system when relevant events happen - such as updates on payments, tokens, scheduled payments, risk decisions or transaction state changes.Why use webhooks?
Webhooks help you:- Receive data automatically when something changes
- Automate workflows (e.g., update order status, trigger refunds)
- Simplify integration - just provide a URL to start receiving events
- Reduce load on your systems by avoiding polling
When not to use webhooks
Webhooks are asynchronous and typically fast, but not guaranteed to be instant. In rare cases - such as during platform releases or data center switchovers - delivery may be delayed by several minutes. If your workflow depends on real-time transaction status, use the Transaction Status query API instead.
Use webhooks when:
- You want event-driven automation
- You need transaction updates for reporting or reconciliation
- A short delay (up to ~15 minutes) is acceptable
Use transaction status query when:
- You need immediate confirmation (e.g., to capture funds or deliver goods while the shopper is present)
- Your workflow is time-sensitive
Availability
Webhooks are typically delivered within seconds of the triggering event. However, during platform releases, data center switchovers, or application restarts, delivery may be delayed by up to 15 minutes.
| Use case type | Recommended approach | Reason |
|---|---|---|
| Real-time decisions (e.g., capture, fulfillment) | Transaction Status query API | Ensures immediate and reliable status |
| Reporting & reconciliation | Webhooks | Delays are acceptable; automation-friendly |
| Non-critical automation | Webhooks | Efficient and event-driven |
| High-frequency polling | Not recommended | Use webhooks or transaction export via SFTP instead |
Retry & failure behavior
| Behavior | Description |
|---|---|
| Timeout | No response within 30 seconds → marked as failed |
| Failure | Non-2xx HTTP response → marked as failed |
| Retry intervals | 1 min → 2 min → 4 min → 8 min → 15 min → 30 min → 1 hour → daily (up to 30 days) |
| Failing retry pattern | Retries pause if all messages fail at a given interval; resume once delivery succeeds |
| Daily failure summary | Email with up to 100 failed notifications per endpoint |
| Retention | Failed messages stored for 30 days, then purged |
Message Ordering & Load
- No guaranteed order: Events may arrive out of sequence. Design your system to handle this.
- Multiple final messages: You may receive more than one final status (e.g., success + failure). Deduplicate based on transaction ID and status.
- High throughput: Your server must handle bursts (e.g., 30+ notifications/sec). Use asynchronous processing and caching where possible.
Configuration
Webhooks are managed in the user interface portal under: Administration → Webhooks.
You can configure webhooks at any level of your entity hierarchy. Each webhook will receive notifications for the entity it’s configured on, as well as all its descendants.
Scope & hierarchy
| Feature | Description |
|---|---|
| Entity scope | A webhook receives events from its entity and all child entities |
| Multiple endpoints | The same notification is sent to all active webhook URLs configured at or above the entity level |
| Filtering | You can filter which types of events (e.g., payments, risks) each webhook should receive |
| Automatic retries | Failed deliveries are automatically retried based on the retry policy |
| Failure alerts | Daily email alerts are sent when delivery fails repeatedly |
Add a webhook
To add a webhook, configure the following parameters:
| Parameter | Description |
|---|---|
| URL | The public endpoint that will receive the webhook notifications |
| Types | Event categories to subscribe to: PAYMENTS, REGISTRATIONS, SCHEDULES, RISKS |
| Fields | Choose ALL (full payload) or NON_CUSTOMER_DATA (exclude sensitive fields) |
| Secret | A 64-character hex string used to encrypt the payload |
| Wrapper | Format of the payload: None (hex string) or JSON (wrapped in JSON) |
| Emails | One or more email addresses to receive daily failure summaries |
Test a webhook
Before activation, you must test the webhook using the "Click to Test" feature. This sends a dummy notification to your configured URL.
The test ensures:
- The URL is reachable
- Your firewall allows incoming traffic
- Your server responds with HTTP 2xx
- The payload is correctly received and decrypted
Format
Webhook notifications are sent as JSON objects with a consistent structure. This allows your system to parse and process them reliably.
{
"type": [notification_type],
"action": [status],
"payload": [content]
}
Field descriptions
| Field | Description |
|---|---|
| type | The category of the event: PAYMENT, REGISTRATION, SCHEDULE, or RISK |
| action | Only present for REGISTRATION events. Indicates the change: CREATED, UPDATED, or DELETED |
| payload | The full content of the event. This mirrors the response you would receive from the corresponding API (e.g., payment, risk transaction) |
The structure is consistent across event types, but the payload schema varies depending on the event (e.g., payment vs. risk)
Example - payment
{
"type":"PAYMENT",
"payload":{
"id":"8a829449515d198b01517d5601df5584",
"paymentType":"PA",
"paymentBrand":"VISA",
"amount":"92.00",
"currency":"EUR",
"presentationAmount":"92.00",
"presentationCurrency":"EUR",
"descriptor":"3017.7139.1650 OPP_Channel ",
"result":{
"code":"000.000.000",
"description":"Transaction succeeded"
},
"authentication":{
"entityId":"8a8294185282b95b01528382b4940245"
},
"card":{
"bin":"420000",
"last4Digits":"0000",
"holder":"Jane Jones",
"expiryMonth":"05",
"expiryYear":"2018"
},
"customer":{
"givenName":"Jones",
"surname":"Jane",
"merchantCustomerId":"jjones",
"sex":"F",
"email":"jane@jones.com"
},
"customParameters":{
"SHOPPER_promoCode":"AT052"
},
"risk":{
"score":"0"
},
"buildNumber":"ec3c704170e54f6d7cf86c6f1969b20f6d855ce5@2015-12-01 12:20:39 +0000",
"timestamp":"2015-12-07 16:46:07+0000",
"ndc":"8a8294174b7ecb28014b9699220015ca_66b12f658442479c8ca66166c4999e78",
"channelName": "OPP_Channel",
"source": "SYSTEM",
"paymentMethod": "CC",
"shortId": "5420.6916.5424"
}
}
Example - registration
{
"type":"REGISTRATION",
"action": "CREATED",
"payload":{
"id":"8a82944a53e6a0150153eaf693584262",
"paymentBrand":"VISA",
"result":{
"code":"000.000.000",
"description":"Transaction succeeded",
"randomField1315125026":"Please allow for new unexpected fields to be added"
},
"card":{
"bin":"420000",
"last4Digits":"0000",
"holder":"Jane Jones"
},
"authentication":{
"entityId":"8a8294174b7ecb28014b9699220015ca"
},
"redirect":{
"parameters":[
]
},
"risk":{
"score":""
},
"timestamp":"2016-04-06 09:45:41+0000",
"ndc":"8a8294174b7ecb28014b9699220015ca_b1539494024c411684b544574716e608",
"channelName": "OPP_Channel",
"source": "SYSTEM",
"paymentMethod": "CC",
"shortId": "7820.6916.2918"
}
}
Example - schedule
{
"type": "SCHEDULE",
"payload": {
"id": "8acda4a489919d63018996faf10b2a66",
"registrationId": "8acda4a889919e5e018996f86a8f127a",
"paymentType": "SD",
"presentationAmount": "92.00",
"presentationCurrency": "EUR",
"result": {
"code": "000.000.000",
"description": "Transaction succeeded",
"randomField1730751282": "Please allow for new unexpected fields to be added"
},
"resultDetails": {
"ConnectorTxID1": "8acda4a489919d63018996faf10b2a66"
},
"customer": {
"givenName":"Jones",
"surname":"Jane",
"merchantCustomerId":"jjones",
"sex":"F",
"email":"jane@jones.com"
},
"authentication": {
"entityId": "8a8294174b7ecb28014b9699220015ca"
},
"redirect": {
"parameters": []
},
"risk": {
"score": ""
},
"timestamp": "2023-07-27 10:52:55+0000",
"ndc": "ef2c099f5b29455a9dbd260c59bcc224",
"channelName": "OPP_Channel",
"source": "SCHEDULER",
"paymentMethod": "DC",
"shortId": "3833.0396.7654"
}
}
Example - risk
{
"type": "RISK",
"payload": {
"id": "8ac9a4a86461239601646522acb26523",
"referencedId": "8ac9a4a86461239601646522aaf96510",
"paymentType": "RI",
"paymentBrand": "VISA",
"presentationAmount": "0.0",
"result": {
"code": "000.000.000",
"description": "Transaction succeeded"
},
"card": {
"bin": "420000",
"last4Digits": "0000",
"holder": "Jane Jones",
"expiryMonth": "03",
"expiryYear": "2025"
},
"authentication": {
"entityId": "8a8294174b7ecb28014b9699220015ca"
},
"redirect": {
"parameters": []
},
"risk": {
"score": ""
},
"timestamp": "2018-07-04 11:52:08+0000",
"ndc": "8a8294174b7ecb28014b9699220015ca_b1539494024c411684b544574716e608",
"channelName": "OPP_Channel",
"source": "SYSTEM",
"paymentMethod": "RM",
"shortId": "3833.0396.7654"
}
}
Encryption
To protect sensitive transaction data from tampering or unauthorized access, all webhook payloads are encrypted before being sent to your configured endpoint. Decryption is required on your side to access the actual event content.
Encryption details
| Parameter | Description |
|---|---|
| Algorithm | AES (Advanced Encryption Standard) |
| Key | 64-character hexadecimal string (configured in the webhook settings) |
| Key length | 256 bits (32 bytes) |
| Block mode | GCM (Galois/Counter Mode) |
| Padding | None |
| Initialization vector | Sent in HTTP header X-Initialization-Vector (hexadecimal) |
| Authentication tag | Sent in HTTP header X-Authentication-Tag (hexadecimal) |
| Payload format | Encrypted hexadecimal string in the body |
| Wrapper option | None (raw hex string) or JSON (e.g., { "encryptedBody": "..." }) |
Payload wrappers
Depending on your configuration, the encrypted payload is delivered in one of two formats:- None (default)
- Content-Type: text/plain
- Body contains only the encrypted hexadecimal string
- JSON wrapper
- Content-Type: application/json
- Body format: {"encryptedBody": "hexadecimal_string"}
Decryption
Decryption is the process of converting the encrypted webhook payload back into its original, readable format. This step is essential to interpret and act on the transaction data securely delivered to your endpoint.
What you need to decrypt
To decrypt the payload, your system must use the same encryption parameters configured in the webhook setup. These include:| Component | Source |
|---|---|
| Encrypted body | In the HTTP request body (hexadecimal string or JSON wrapper) |
| Secret key | Configured in the webhook settings (64-character hex string) |
| Initialization vector | In HTTP header X-Initialization-Vector |
| Authentication tag | In HTTP header X-Authentication-Tag |
| Algorithm | AES-256-GCM (no padding) |
Decryption steps
- Extract the encrypted body from the request (either raw or from the encryptedBody field in JSON).
- Read the initialization vector and authentication tag from the headers.
- Use the configured secret key to decrypt the payload using AES-256-GCM.
- Validate the authentication tag to ensure integrity.
- Parse the decrypted payload as JSON to access the event data.
Common pitfalls
- Ensure the key format and length match the expected AES-256 requirements.
- Use UTF-8 encoding when converting strings to bytes.
- Validate the authentication tag to prevent tampered data.
- Handle decryption errors gracefully to avoid processing invalid payloads.
Example - decryption

Response handling
When your system receives a webhook, it must respond with a 2xx HTTP status code (e.g., 200 OK). This confirms successful delivery and prevents retries.
If your server:
- Returns a non-2xx status code, or
- Fails to respond within 30 seconds,
Protocol requirements
To decrypt the payload, your system must use the same encryption parameters configured in the webhook setup. These include:| Parameter | Requirement |
|---|---|
| Protocol | HTTPS (TLS 1.2 or higher) |
| Method | POST |
| Content-type | text/plain or application/json (based on wrapper) |
| SSL certificates | Must be valid and trusted; self-signed certificates are not accepted in production |
Best practices
- Ensure your endpoint is always available and can respond quickly
- Use asynchronous processing to avoid delays in response
- Monitor for non-2xx responses and fix issues promptly
- Validate that your SSL certificate chain is complete and trusted