# 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

```json
{
  "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

```json
{
  "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"
}
```

```json
{
  "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

```json
{
  "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"
}
```

```json
{
  "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"
}
```

```json
{
  "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

```json
{
  "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"
}
```

```json
{
  "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)"
}
```

```json
{
  "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

```json
{
  "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

```text
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

```json
{
  "typ": "JWT",
  "kid": "98d5e08e-f53e-4821-bfdd-3f60fb0a4d08",
  "alg": "RS256"
}
```

Example JWT Payload

```json
{
  "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

```json
{
  "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

```json
{
  "iat": 1657569050,
  "request_body_sha256_hash": "B8B0D4618D9F7D193DF0AA8132F63566BB71D57CD3DB87FB44D0C110E0103666"
}
```
