How to set up webhooks

Learn how to configure and optimize webhooks for your integration.

Determine what events to subscribe to

To minimize unnecessary burden on your server, only subscribe to events you need. See our webhook events guide for a full list of events and payload examples.

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

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.

Create test webhook

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

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

1
2
3
4
5
6
7
8
9

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

Verify events were sent by Moov

Check out our example webhook handler project 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.
  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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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

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

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 – each subscribed to specific event types.

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.

Summary Beta