Webhooks
Webhooks
Webhooks are used to notify of status changes to a configured endpoint. The webhook details can be configured in the Developer area of Portal at the aggregator or payer level. If a non 20x response is returned, we will retry the webhook 2 additional times in 15 seconds, then repeat the cycle again within the next 2 hours.
Webhooks are sent for the following events:
- Payment status updates
- Transaction status updates
- Account status updates
- Prepaid Account created via API
- Card Account successfully added via Widget UI
- Bank Account successfully added via Widget UI
- Bank Account status updated upon Chargeback/Reversal request
Webhook Idempotency
Webhook payloads will include a header x-webhook-id with a unique value that corresponds with the Aws-Api-Gateway-Requestid header response value from the originating Create request.
Webhook Payload schemas
PAYMENT WEBHOOK PAYLOAD
Payload
{
"webhook_type": "PAYMENT",
"payment_id": "String : Payment ID",
"payment_status": "Enum : Payment Status",
"reference_id": "String (optional) : Reference ID if included"
}
TRANSACTION WEBHOOK PAYLOAD
Transaction Update Transaction Update w/ Error Code
{
"webhook_type": "TRANSACTION",
"type": "Enum : Transaction Type",
"method": "Enum: Transaction Method",
"transaction_id": "String : Transaction ID",
"transaction_status": "Enum : Transaction Status",
"reference_id": "String (optional) : Reference ID if included",
"webhook_created_date": "Timestamp",
"network_approval_code": "String : Approval code for Instant Deposit"
}
{
"webhook_type": "TRANSACTION",
"transaction_id": "String : Transaction ID",
"transaction_status": "Enum : Transaction Status",
"reference_id": "String (optional) : Reference ID if included",
"error_code": "String: error code",
"error_message": "String: error detail"
}
PAYMENT ACCOUNT WEBHOOK PAYLOAD - Bank Account
Bank Account Created Bank Account Status Update Bank Account Failure
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"account_number": "String : Last 4 of Account Number",
"account_type": "String: CHECKING or SAVINGS",
"bank_name": "String"
}
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"account_status": "String : Status of Bank Account"
}
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"error_code": "String: error code",
"error_message": "String: error detail"
}
PAYMENT ACCOUNT WEBHOOK PAYLOAD - Card Account
Card Account Created Card Account Failed due to fraud controls Card Account Created with Account Verification
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"card_number": "String Masked card number"
}
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"card_number": "String : Masked card number",
"error_code": "String : Error code for account sharing (ERR_ACCOUNT_SHARING_LIMIT_EXCEEDED)"
}
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"card_number": "String Masked card number",
"verification_result": {
"network": "String: VISA/MASTERCARD",
"pan": "String: Match result",
"cvv": "String: Match result",
"avs": {
"street": "String: Match result",
"zip": "String Match result"
},
"ani": {
"first_name": "String: Match result",
"last_name": "String: Match result"
}
}
}
PAYMENT ACCOUNT WEBHOOK PAYLOAD - Prepaid Account
Prepaid Account Webhook
{
"widget_id": "String: Widget ID",
"webhook_type": "PAYMENT_ACCOUNT",
"account_id": "String : Account ID",
"recipient_id": "String : Recipient ID",
"prepaid_account_status": "String : Account Status",
"prepaid_account_error": "String : Account Error Detail"
}
Webhook Verification
Webhook payloads include an x-verification header that contains a JSON Web Token (JWT) to verify the webhook contents and origin. It is recommended that the webhook signature and contents are verified before processing the payload.
Using the JWT to Verify Webhooks
1. Decode the x-verification header JWT
Use your library of choice to decode the x-verification header JWT.
Example x-verification header
x-verification : eyJ0eXAiOiJKV1QiLCJraWQiOiI5OGQ1ZTA4ZS1mNTNlLTQ4MjEtYmZkZC0zZjYwZmIwYTRkMDgiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2NTc1NjkwNTAsInJlcXVlc3RfYm9keV9zaGEyNTZfaGFzaCI6IkI4QjBENDYxOEQ5RjdEMTkzREYwQUE4MTMyRjYzNTY2QkI3MUQ1N0NEM0RCODdGQjQ0RDBDMTEwRTAxMDM2NjYifQ.Kzb-awXSL0bZ0vldAP_VHQ8N3P_TMwaz8b2WICX9Kb1tgYhoIPxpEtTm6zRv3LEAZTHtegsiM5AR8vxK5JSJgzInOicUpG-JSbuHzgX6ofWHFrPJtGnHPOwxJn54s7Z2MYTe1AnSsgA_86IxD8Tqgka_UZAyr__KJigqONRaVxiEdGuMjrn61J-sbKefvCwqJUtP2HuMYZDf3dMSzo0NI6zWBeskPueaJXSuAjLRAWVwbnmOTYbGFxe2GhwQTkcLCGqtDwE2YPm3xp9zc30ite8Xy5JKxiSZr17QNE6oTFDKW9ykcg8QMZJoNu6WO4kdFDsyTcBRTJMmEaWifrud9VaVT1-GqoOsKlPIBE0l3dmaAajxNyeOXkbinpcIvxLkn291_sKRgPAbzajRT9DuTuR5YzuqV9uQYKKCWhkpPDcYs16kbOzmZCKstUCDldV5yIx1vCF3iq8A_S56RIgdr4D4_LNPyFSeWkf-e7ZOCcdXyOvB8n-X2o-70Gte0Tx-gPT6sr0MW2CESEVOxPe-MSxs54_Ns1yQc5LLCtuYMpzssjTy3JaLJvNFVP2Gw2EqsGY7cJEqSy2YGXCe1kDpCA889dnzXnwGTralCxyWgfDotqu48zZ6v1cBjlWNZGh_I9YwJvIUpRyp9VBSi2QzCfWNBeHoo5xjF0lUVC1h5dU
Decoding the example JWT x-verification header results in the following JWT header and payload:
Example JWT Header
{
"typ": "JWT",
"kid": "98d5e08e-f53e-4821-bfdd-3f60fb0a4d08",
"alg": "RS256"
}
Example JWT Payload
{
"iat": 1657569050,
"request_body_sha256_hash": "B8B0D4618D9F7D193DF0AA8132F63566BB71D57CD3DB87FB44D0C110E0103666"
}
2. Get the Validation Key
Make a GET request to https://test.api.interchecks.io/api/v2/{payerId}/webhooks/get_validation_key/{kid}, replacing {payerId} with your payer id, and {kid} with the kid obtained from the JWT header. The response contains a public key to verify the authenticity of the JWT payload.
Example Get Validation Key Response
{
"kty": "RSA",
"e": "AQAB",
"n": "ylNCSJtagLrhi2D0aITP8mdFl6sAo4LPePyUgqejemKdGQPtH1YDKuetMSTv5nDbMjdGPxA6VqJPAFFnMdbz3q4pYBPFTo18pMw7bbKN9P7tS_eQ6KZABclz2Fy7oyrJBv3NdVVNpEW8CkrJr8R3CUIpkJtv_z-hV7PMLxmBDDaDnU0bf4q_mFCAbk2uXc32CNxVntQL1bULKg5YYE6pJrpMpZ6pf7UuWF1Gw7OFVS1OBa9gcu_hzHR015Mu12b4HRNCDzULR3PBdM9XvjmXIk97AjWpPPGiHNUwflfEHGMf2bMIbM63tTYdi3yZIqTn8VeZRjym3Dw6gSj11WK_FEiNmHRzTmhw43iOU7Mq8rd0mSccBOEr1XyD2TkQ1ttFz5qgDTUTfzlucshCTkkiF8p8V_waKtb_5U5sxtZ65a4Xs_9HkIfIrEIXZzLYOeEInEDD1h_UWrOu8DrZN7l6SliYbnD0nBqLxszN9k17ZsA-PbrUyuTmrNj3m8puVTZWjpFbWQr_aKhQMBsbjkDksVb2oae0QKgwlB5qwkf4wb-hGzviGx0fpjHFcwomU1pU1trGgBQFXt33vTi-IRsrgBczOw0d0vLHhmqlnWlaHpSFIcYA63HBuijcgwK3Ufqc71-ytwd4HRpEhWxhPZXK0KgIRt2yVDoKgeyEkpE7FIM"
}
3. Verify the Authenticity of the JWT
Use the key obtained in step 2 to verify the signature of the JWT. Your JWT library should provide a convenient method to handle the verification. If the signature is not valid, do not process the webhook.
4. Read the JWT Payload and Confirm the Webhook Hash.
Finally, parse the contents of the JWT payload, example again below. The two keys, iat and request_body_sha256_hash are used to verify the time the webhook was sent and the hash of the webhook body. Compare the request_body_sha256_hash (the hash itself is case insensitive) with the SHA-256 hash of the webhook body to confirm they match. If these hashes don't match, do not process the webhook.
The iat value is the timestamp in seconds indicating when the webhook was created. For security reasons, the webhook should be rejected if the webhook is more than 5 minutes old. If verification is successful, process the webhook payload.
Example JWT Payload
{
"iat": 1657569050,
"request_body_sha256_hash": "B8B0D4618D9F7D193DF0AA8132F63566BB71D57CD3DB87FB44D0C110E0103666"
}