It is important to note that not all webhook payloads are encrypted - this depends on your configuration. If you wish to use encryption you must provide a public key for your webhook configuration within the console. For specific information on the webhook payload schema and fields, you can view the specific detail under the following pages:
Table of Contents
Our encryption Process
Here are our encryption steps which will be beneficial to help you understand what needs to be done for the decryption steps:
Generate random AES-256 key (32 bytes)
Generate random IV (16 bytes) for AES-CBC
Encrypt payload data with AES-256-CBC using the key and IV
Encrypt the AES key with RSA-OAEP-SHA256 using your public key
Encode all components as Base64 strings
Return encrypted JSON payload containing
encryptedKey,data, andiv
Payload Structure
When using encryption, the payload will always follow the following structure:
plaintext
{
"encryptedKey": "base64-encoded-encrypted-aes-key",
"data": "base64-encoded-encrypted-payload-data",
"iv": "base64-encoded-initialization-vector"
}Field Descriptions
| Field | Description | Encoding | Size |
|---|---|---|---|
encryptedKey | AES-256 key encrypted with RSA-OAEP-SHA256 | Base64 | ~344 chars (2048-bit RSA) |
data | Payload data encrypted with AES-256-CBC | Base64 | Variable (depends on payload size) |
iv | Initialization Vector for AES-CBC | Base64 | 24 chars (16 bytes) |
Decryption Specifications
Step 1: RSA Decryption (AES Key Recovery)
Purpose: Decrypt the AES key using your RSA private key
Algorithm: RSA with OAEP padding
Hash Function: SHA-256
MGF: MGF1 with SHA-256
Label: None (empty)
Process:
Base64 decode the
encryptedKeyfieldUse RSA-OAEP-SHA256 decryption with your private key
Result: 32-byte AES-256 key
Step 2: AES Decryption (Data Recovery)
Purpose: Decrypt the payload data using the recovered AES key
Algorithm: AES-256-CBC
Key Size: 256 bits (32 bytes)
Block Size: 128 bits (16 bytes)
Padding: PKCS#7 (also known as PKCS#5 in some implementations)
Process:
Base64 decode the
dataandivfieldsValidate IV is exactly 16 bytes
Use AES-256-CBC decryption with the recovered key and IV
Convert resulting bytes to UTF-8 string
Implementation Examples
JavaScript (Node.js)
plaintext
const crypto = require('crypto');
function decryptWebhookPayload(encryptedPayload, privateKeyPem) {
// Step 1: Decrypt AES key with RSA-OAEP-SHA256
const aesKey = crypto.privateDecrypt({
key: privateKeyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256'
}, Buffer.from(encryptedPayload.encryptedKey, 'base64'));
// Step 2: Decrypt data with AES-256-CBC
const iv = Buffer.from(encryptedPayload.iv, 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', aesKey, iv);
let decryptedData = decipher.update(encryptedPayload.data, 'base64', 'utf8');
decryptedData += decipher.final('utf8');
return decryptedData;
}Python
plaintext
from cryptography.hazmat.primitives import hashes, padding, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding as asym_padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import base64
import json
def decrypt_webhook_payload(encrypted_payload, private_key_pem):
# Load private key
private_key = serialization.load_pem_private_key(
private_key_pem.encode(), password=None
)
# Step 1: Decrypt AES key with RSA-OAEP-SHA256
encrypted_aes_key = base64.b64decode(encrypted_payload['encryptedKey'])
aes_key = private_key.decrypt(
encrypted_aes_key,
asym_padding.OAEP(
mgf=asym_padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Step 2: Decrypt data with AES-256-CBC
iv = base64.b64decode(encrypted_payload['iv'])
encrypted_data = base64.b64decode(encrypted_payload['data'])
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
decryptor = cipher.decryptor()
padded_data = decryptor.update(encrypted_data) + decryptor.finalize()
# Remove PKCS7 padding
unpadder = padding.PKCS7(128).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()
return data.decode('utf-8')Testing Your Implementation
Use this test payload to verify your implementation:
Test Payload:
plaintext
{
"encryptedKey": "nWjkuaqpfvUKUwUfTO97iMjBl03Mtl7AfWIjt06vrBg+vSfHdIoR2kBtFv3Pm5BljBmTCz5FLM4gVC5SaME40zSNhpOK1oT2M/vw7j+OzgxF8k5ZePECbtDvaljNDOXkkIW6P4LcjY7uUbm1A0vkcgkG+RdGLpyWd4jdu3sf7cSz+pdjv0mJwtEbk65yYW00Q3mFhcjKl+hR8TISzkJDxS73gkEZMQZZs51wQYj4drmddHxxZchE+tM2z99bFt8q5cZZVBhqrukBkHptQGBpdGh2vWaOqRsWiapw2gkq/R5wi0aH/hkLCP2lDoni1ZBEiNG8RuYMb3s1pSWZZoU0Og==",
"data": "Uey9MVc0RaXms1i7GfbTuR0LwrdFARsYb++hwUDwBPyA/TGLpHiUGW9e/NPeaFaFyLiPjH6JVgoIvOo285H1InZaiEYfBIlU21h47tapJPCpOxOX8IC/1dRVHIXX2CdPk4+jyNA4IL4MpVHEAJWXTECa6sYt3z1ePM8ppHLV88CzRkbPJ5TzuHfd2+8tehe5egEudC+i8dYlbU2fEywTfuff3U36ON3SgdmW3k26RI+9oWff7bEq9lnuB3D3rai4UWXX4+v0wa1iJvZsDPumA+GHGpGSs6/HktH39dlcVB/bhdtRsNQeRtnjV1P5I/X/YT/ZzmaXvh7+hKCwby2DkukzIEIO3tTtOK6sJWrxkm0uE03tiv9517XFUHO45gII",
"iv": "u8JKSIVAqtYXRjQvfKqvqg=="
}Test Private Key (Demo Key Only - Do Not Use in Production):
plaintext
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDR7rACQBtqJvNa
wWoR20owygssGgeGCAF5t6vdXeXhnz/Sgq6xXb9hVU6zDg1AxoIYMS9zWGQGO9YU
gqUd11KFJeM1sAc8GqQMgHzFf6XYmEWDUBupiJ6k9FNIzo33UGErCZQtB8Tv50fZ
azPtgyE8V07cirAD3o3j4PVgZoF3DHVLLf8SMUKZGu2zJzWokT3W/C7Ih1s5s+tb
rQb5pKLBJd9d0RBc46+BEZyVGjelTcScVmhMhHoLYAGeq8vZvEWi0JaUqfu2uhRA
/ZjDYdHb0t3vt7pPMHl1KSay4kSm/upFcTejlCT7U8ujcOb0osW6HuQySHS8A19g
1KqwXPy3AgMBAAECggEAOBYB1ggUiu6vE28bDHw+vPwtsfGeUvOumTs14mVktjua
jH43d9FpAPMOnI3DgqvGM/poB/P0fSndTwt8W5WcSVBO/Jfzxt4DOAfXzdgFkfFU
mPl8+nUylTVlTs1IZmuGGj5r5P/vYHy02GRosirXZUyaGBfBYLdDxz7hr4iTTB9+
ye9qtVnDEZQHn+VxvC2T0u6eUOlQRLUDoF5U6rJPQGtIaNqe2vEiB22yEjdUxiZk
7h6/lpey6SL9/PKedGO8rdBHfRDfHrY4WuVEHZ9QuDtfFZf8dAAX2nnY462iv4iJ
Lq9XT6AHa0ffwgoyyk1j/LnORK7UXvUDZXDSTuZTmQKBgQD4LoaTud68vcrUZWYD
RGX9KFXo0pv49N0AbI4TH2lpQbau01dY3diWfg6uLYWJaq8JBL3XWNHlRFtuB7SB
TEFW7rfjMP8lzrx1gkE+oHv4azmlinTQSqSn/acbraB85y4NrFWdTmPCUbKI8mZV
R6kFmRnbXYX31XIWx+E8+rn2nQKBgQDYi7KnS3Lufp0tlY7CdUGjMQEcOXHlGQfd
NsPBwqTvgS5ZBkz7CYx61UBnzfOWAm3Lgck6tkU1VWYrk5ClQvQyzOE5W2L0rMy5
8ENGZkesgFgUMcAZUSrHy0MBoEp5O1SSgo30RHFj0+jjc4f57lu40RbjCwvXRsh1
UQ5Vv+u2YwKBgQDWRh0tGFJFtLuVK8Kq2XgQ3a1mqce+Z3MT9aWovvscxH68bH3C
nS9tp4J3QZhEr5ZV/AEIF4iJIL6rvf1LkRHsw4iCn//6nYa7Ee3Q63B9Z95KncMD
Px7wZ6DK1dp1XIe3iG4WteKNmEnD0T0nLgqxaaEYc6PB+UhagNJn+rVqRQKBgQCX
kbCKyJS5uoXp0jUKh2haAYL1bn/6C3slq595j/RCb/kw9ugLDrP1hi5+efl1ll2j
hESFg+8NVakd9zvXcPXcWOuMahrph8oEm9d6/zxL0cnU2L3M+rMnBW7xRHDcSlr3
8cnKn91+MmVgTw1uMxigVDng0WqIVFMbSpDuFFe4YQKBgBASvEVfxeH3EvhX6sx2
t8tvjY3BnjyEh6MX4radKoJZKB1syp9VZf6xdawiUcXuBTaets5/ELY4Xo50Rixc
uOcR+baFN7nNYb3Itj5rP6JWbxmpUWQSa8R/S5aWYhh1FH6R6XNX3GZxV7e6xXC4
3TKD4TIyz7Gs0F+TPLrmBNG3
-----END PRIVATE KEY-----Expected Decrypted Result: ✅ Success Indicator: If your implementation is working correctly, you should see the decrypted JSON payload below when you decrypt the test payload using the provided private key.
plaintext
{
"event": "account_connected",
"accounts": 1,
"customer_id": "customer177",
"journey_key": "bce79918-6af1-4b8c-93df-f68091d3a5c2",
"date": "2025-10-27T16:17:53.261Z",
"url": "http://localhost:3001/consumer/066dd6b6-809f-4f75-8aa1-7ed9e306e16e",
"journey_name": "Journey 6 (Embedded journey)"
}Security Considerations
Key Management
Never hardcode private keys in your application code
Use secure key storage solutions (HSM, key vaults, encrypted storage)
Ensure proper access controls and key rotation policies
Store keys in environment variables or secure configuration systems
Memory Security
Clear sensitive key material from memory after use
Be cautious about logging decrypted content
Validate input parameters before processing
Validation Requirements
Verify RSA key size: Minimum 2048 bits recommended
Validate field presence: Ensure all required fields exist
Check data lengths: AES key (32 bytes), IV (16 bytes)
Handle errors gracefully: Don't leak cryptographic information in error messages
Troubleshooting
Common Issues
"OAEP decoding error" or "RSA decryption failed"
Cause: Wrong private key or mismatched padding/hash settings
Solution: Verify you're using the correct private key and RSA-OAEP-SHA256
"Invalid initialization vector"
Cause: IV is not exactly 16 bytes after Base64 decoding
Solution: Verify IV Base64 decoding and length validation
"Bad decrypt" or "Padding error"
Cause: Incorrect AES key or corrupted encrypted data
Solution: Ensure AES key was decrypted correctly from RSA step
"Invalid key length"
Cause: AES key is not exactly 32 bytes (256 bits)
Solution: Verify RSA decryption produced correct key length
Algorithm Reference
RSA-OAEP-SHA256 Parameters
Padding: OAEP (Optimal Asymmetric Encryption Padding)
Hash Function: SHA-256
Mask Generation Function: MGF1 with SHA-256
Label: None (empty/default)
AES-256-CBC Parameters
Algorithm: AES (Advanced Encryption Standard)
Key Size: 256 bits (32 bytes)
Mode: CBC (Cipher Block Chaining)
Padding: PKCS#7 (equivalent to PKCS#5 for AES)
IV Size: 128 bits (16 bytes)
For additional support or questions about webhook payload decryption, please contact our technical support team.
