# Webhooks
source: https://developer.mastercard.com/open-finance-data/documentation/event-notifications/index.md

Webhooks are notifications about particular events sent from the server to the clients. In technical terms, a webhook is a web request that the server sends when specific events happen to a web address belonging to the client. The request is comprised of a JSON body that includes information with more details about the event.

We use the term "notification" and "webhook" interchangeably in our documentation.

#### Create a webhook endpoint {#create-a-webhook-endpoint}

The first step is to create an endpoint on the client's web service that:

* Can receive a POST request with a JSON body
* Can acknowledge the call by returning a 200 status code
* Is public so that it can be called from our servers

These will allow the client's web service to receive webhooks with a payload allowing the client to act on notifications.

#### Acknowledgment of notification {#acknowledgment-of-notification}

To acknowledge that a notification has been received, a 200 HTTP status code must be returned within a timeframe. All other response codes indicate that the webhook has not been received successfully.

It is important to acknowledge the notification as soon as possible so that we know that it has been received. Otherwise, we will retry the notification attempt six times over a 24-hour period using an exponential backoff. We encourage returning a 200 HTTP status as soon as possible before executing any complex logic in order to avoid possible timeouts.

##### Message Retry {#message-retry}

Failed messages are retried over a 24-hour period using an exponential backoff as follows:

| **Retry Attempt** | **Time since first attempt** |
|-------------------|------------------------------|
| 1                 | 6 seconds                    |
| 2                 | 48 seconds                   |
| 3                 | 5 minutes                    |
| 4                 | 34 minutes                   |
| 5                 | 3 hours 42 minutes           |
| 6                 | 24 hours (final attempt)     |

#### Register webhook URL {#register-webhook-url}

Webhook URLs are configured during client onboarding.

Ensure that the webhook endpoint is configured to use HTTPS with a valid certificate.

### Webhook Structure: Message Headers {#webhook-structure-message-headers}

**Content-Type:** Indicates the format of the data within the webhook body. This will always be set to "application/json".

**X-Mastercard-Signature:** This header contains a unique hash of the webhook payload. Uniqueness is provided by combining the message payload with the timestamp of signature generation. See the section of Validating Webhook Signatures for more information.

**X-Mastercard-Signature-Timestamp:** This header indicates the time, in milliseconds, of when the signature was calculated. This is used to help prevent replay attacks.

**X-Mastercard-Signature-Algorithm:** This header indicates the hashing algorithm used for generating the message hash for the webhook. Currently, this service only supports "SHA256withECDSA."

**X-Mastercard-Signature-Version:** This header indicates which version to use when calculating the message signature. This header is provided for forward-compatibility as new algorithms are added.

**X-Mastercard-Webhook-Message-Id:** A unique identifier (UUID) for each webhook message. Importantly, if a message is re-attempted, each delivery attempt will have a unique message ID.

**X-Mastercard-Signature-Verification-Key:** The unique fingerprint of the signature key used when calculating the value of the X-Mastercard-Signature header. If multiple signing keys have been registered for use within the webhook system, this header is essential for determining which signing key is being used.
Tip: When received, HTTP headers may be presented as lowercase strings. The above documentation is capitalized for readability.

### Webhook Structure: Message Body {#webhook-structure-message-body}

The body of the webhook event will contain the data generated by the webhook, as a string. The receiving application will be responsible for reading the Content-Type HTTP header and parsing the webhook data accordingly.

Each webhook event type will contain its own unique structure. Please consult the documentation for the desired webhook event for more information regarding its particular structure.

### Message Verification {#message-verification}

Several steps should be taken when verifying webhooks received.
Firstly, the `X-Mastercard-Webhook-Message-Id` header may be checked against a list of previously received
webhooks to guard against replay attacks.

### Verifying Message Signature {#verifying-message-signature}

To verify the identity of the sender (i.e., Mastercard), it is important that the receiving application
perform a hashing of the webhook body, concatenated with the value of `X-Mastercard-Signature-Timestamp`,
and comparing it to the value of `X-Mastercard-Signature`.
The value of `X-Mastercard-Signature-Algorithm` indicates which algorithm to use when generating the message hash.
In pseudo-code, this can be expressed as:

    messageHash = hashingFunction(webhook-message + "." + webhook-timestamp)

The default signature verification key is generated using ECDSA with SHA-256.
You can extract the corresponding public key from the X.509 certificate obtained during onboarding.
Please note that the keys used for webhook signature verification
are different from those used for API authentication.

With that information, the following code snippet is an example of how message verification may be performed.

```js
// Nodejs
const crypto = require('node:crypto');

const HASH_ALGORITHM = 'sha256';
// Loaded at startup, or selected based on X-Mastercard-Signature-Verification-Key header
const PUBLIC_KEY = process.env.PUBLIC_KEY;

/**
 * @param {string} signature - Provided via X-Mastercard-Signature header
 * @param {string} webhookMessage - Webhook body
 * @param {number} timestamp - Provided via X-Mastercard-Signature-Timestamp
 * @returns {boolean}
 */
function isValidWebhook(signature, webhookMessage, timestamp) {
  const verify = crypto.createVerify(HASH_ALGORITHM);
  verify.write(`${webhookMessage}.${timestamp}`);
  verify.end();
  return verify.verify(PUBLIC_KEY, signature);
}
```

### Verifying Timestamps {#verifying-timestamps}

Additional checks for validating the timestamp should be performed.
To mitigate replay attacks, webhooks with unreasonably old timestamps should be discarded.
The threshold for valid timestamps is to be determined by the receiving application,
but under normal circumstances webhooks older than 60 seconds should be rejected.

### HTTP Delivery Considerations {#http-delivery-considerations}

All webhook requests are sent using HTTP POST. The application receiving the webhook is expected to respond with an HTTP 200 or 204 to indicate successful receipt of the webhook. All other HTTP status codes will be considered errors and the message will be re-queued for delivery. Importantly, any status code indicating a redirection (e.g. HTTP 307 or 308) will be rejected and re-queued.

## Tips and Best Practices for Receiving Webhooks {#tips-and-best-practices-for-receiving-webhooks}

### Close connections early {#close-connections-early}

Webhooks can create spikes in HTTP calls to receiving applications. If not handled properly, these spikes can exhaust available TCP sockets, creating bottlenecks on the network. It is recommended that receiving application respond to the webhook message with an HTTP 200, 202, or 204 immediately upon receipt, before any processing is performed on the webhook payload, including signature validation.

### Fail silently {#fail-silently}

A key in reducing abuse to endpoints exposed for webhooks is to remove any unnecessary response information. In the case of webhooks, the sender only needs to know if the payload was received, not if the data was processable. Returning status codes such as HTTP 400 when a webhook does not follow an expected format does not provide any actionable information to legitimate webhook senders but allows a potential attacker to further refine their attacks. Any errors, such as unexpected payloads, should be logged but not returned on endpoints designed to receive webhooks.

In cases where the receiving application is under heavy load and cannot process more requests, it is advised that the application return a 4xx error code, such as HTTP 429 Too Many Requests, which will cause the webhook sender to reschedule the webhook for a later time.

### Queue messages internally {#queue-messages-internally}

In cases where a large volume of webhooks is expected, it may be desirable to queue received messages for process using something like RabbitMQ. This can reduce the chance of webhooks failing to be received as servers become congested with webhook traffic. If queueing is to be used, it is advised that all webhook messages are verified before being placed into the queue to prevent invalid or malicious webhook payloads accidentally being processed.

### Check for expected algorithms {#check-for-expected-algorithms}

The information provided via the webhook headers should be treated as informational only. Information such as the algorithm being used should be compared against an expected list prior to processing the webhook and should never be used directly.

    // Bad
    const verify = crypto.createVerify(headers['X-Mastercard-Signature-Algorithm']);

    // Good
    if (headers['X-Mastercard-Signature-Algorithm'] === 'SHA256withECDSA') {
      const verify = crypto.createVerify('SHA256withECDSA');
    }

