# How to set up webhooks

Learn how to configure and optimize webhooks for your integration.

## [Determine what events to subscribe to](#determine-what-events-to-subscribe-to)

To minimize unnecessary burden on your server, only subscribe to events you need. See our [webhook events](/guides/webhooks/webhook-events/) guide for a full list of events and payload examples.

## [Create a webhook endpoint on your server](#create-a-webhook-endpoint-on-your-server)

Set up an HTTPS endpoint that accepts and processes HTTP `POST` calls. A URL with HTTPS is required.

## [Register your endpoint in the Moov Dashboard](#register-your-endpoint-in-the-moov-dashboard)

In the Moov Dashboard, navigate to **Developers** and select **Webhooks**. There, you can include your endpoint URL, an optional description, and which events you’d like to subscribe to.

## [Test the endpoint](#test-the-endpoint)

In the Moov Dashboard, you can trigger a test event to your webhook by selecting the **Send test webhook** action.

![Test webhook](../images/send-test-webhook.png)

The target server URL will receive a test payload that looks like this:

```json

  {
    "eventID": "d9d18a42-d1ea-4e4c-b671-0fa93e24d584",
    "type": "event.test",
    "data": {
      "ping": true
    },
    "createdOn": "2024-01-26T20:42:25Z"
  }
```

## [Verify events were sent by Moov](#verify-events-were-sent-by-moov)

Check out our example [webhook handler project](https://github.com/moovfinancial/webhook-handler) on GitHub.

Every event Moov sends to a webhook endpoint includes a signature which allows you to verify that Moov (and not a third party) sent these events to your service.

Use the following steps to construct your hash and compare it against the event signature:

1. Get the signing secret from the [Moov Dashboard](https://dashboard.moov.io/developers/webhooks).
2. Get the following header values from the received `POST`:
   
   - `X-Timestamp`
   - `X-Nonce`
   - `X-Webhook-ID`
   - `X-Signature`
3. Prepare the string: `{X-Timestamp} + "|" + {X-Nonce} + "|" + {X-Webhook-ID}`.
4. Calculate the expected signature using the hashing algorithm HMAC-SHA512 with the signing secret and string from step 3.
5. Check the expected signature matches the value set in `X-Signature`.

If the hash you created matches the value of the `X-Signature` header, you know that the event came from Moov. Otherwise, your service should discard the event.

See the example below:

```jsx
const hmacSHA512 = require("crypto-js/hmac-sha512");

// Get your signing secret from dashboard.moov.io
const webhookSecret = process.env.WEBHOOK_SECRET;

// Check if the hash of headers match the signature
const isSigned = (timestamp, nonce, webhookId, signature) => {
  const concatHeaders = `${timestamp}|${nonce}|${webhookId}`;
  const checkHash = hmacSHA512(concatHeaders, webhookSecret);

  return signature === checkHash.toString();
}

// Serverless function 
exports.handler = async (event, context, callback) => {
  if (!event.body) {
    console.log("Invalid request");
    callback(null, {
      statusCode: 400,
      body: "Invalid request"
    });
  }

  if (!isSigned(
    event.headers["X-Timestamp"], 
    event.headers["X-Nonce"], 
    event.headers["X-Webhook-ID"], 
    event.headers["X-Signature"])
  ) {
    console.log("Signature is invalid");
    callback(null, {
      statusCode: 400,
      body: "Signature is invalid"
    });
  }

  let webhook;
  try {
    webhook = JSON.parse(event.body);
  } catch (err) {
    console.log("Invalid JSON");
    callback(null, {
      statusCode: 400,
      body: "Invalid JSON"
    });
  }

  // Logs the event message payload

  console.log(event.body);

  callback(null, {
    statusCode: 200
  });
};
```

## [Best practices](#best-practices)

We recommend implementing the following best practices when using webhooks with Moov.

### [Subscribe to a minimum number of events](#subscribe-to-a-minimum-number-of-events)

To optimize performance and avoid overloading your server, subscribe to a minimum number of events. Alternatively, you can set up multiple webhooks subscribed to specific event types.

### [Respond within 5 seconds](#respond-within-5-seconds)

If Moov does not receive a `2xx` response from your server within 5 seconds, we’ll consider the delivery of the webhook event as failed. Moov will retry the webhook multiple times, for up to 24 hours. If webhook servers return a `404` after the last retry, the webhook will become disabled.

### [Employ Rate Limiting](#employ-rate-limiting)

If Moov receives a `429` response from your server, we will respect the `Retry-After` response header, retrying delivery of the webhook after the specified delay. This allows you to rate limit webhook requests and manage the load on your server.

In the following example, Moov would retry the request after 1 hour (3600 seconds):

```fallback
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 3600
```

If the `Retry-After` header is not provided with a `429` response, we will follow our default retry timeouts, backing off and retrying for up to 24 hours.
