Get API Keys

Step 2: Processing & Validating Webhooks #

Before we begin, although not required, please note that many of the snippets we use include Amazon Web Services (AWS) as a dependency — specifically, Lambda and SQS. We will use AWS' v2 Node SDK; however, v3 is also currently available.

Now that you have created a webhook subscription in the previous step, we will work on creating a webhook listener/handler. In order to asynchronously process webhooks, we recommend implementing the four following steps: ingestion, authentication, queueing and processing.

Ingestion #

Receive the Webhook #

At its core, a webhook listener is an HTTP endpoint that Dwolla will call. As such, your endpoint must:

  • Be publicly accessible (Dwolla cannot send requests to localhost)
  • Be able to receive POST HTTP requests (Dwolla will not send GET, PUT, PATCH, etc.)
  • Have TLS enabled (with a valid SSL certificate issued to your domain)
  • Be able to handle at least 10 concurrency requests, unless a lower value has otherwise been configured for your application by Dwolla

Return HTTP 2xx Status Code #

Once a webhook has fired to your endpoint (and you have received it), in order to fully "ingest" the webhook, you must respond to Dwolla with a 200-level status code (e.g., 200 OK). Additionally, Dwolla must receive your response within 10,000 milliseconds (or, in other words, 10 seconds). If a response is not received in the allotted time, Dwolla will retry the request up to 8 times over the next 72 hours, according to our back-off schedule. Finally, if your application fails to response after 400 consecutive attempts and 24 hours has passed since the last success, your webhook subscription will automatically pause.

Authentication #

When you set up your webhook subscription, you sent over a secret value to Dwolla. This value is a shared secret that only your application and Dwolla should have access to. Before Dwolla sends a webhook, the JSON-encoded payload is used in conjunction with the shared secret to create the X-Request-Signature-SHA-256 header value, which is sent with the webhook request.

When a webhook request is received, Dwolla recommends creating a SHA-256 HMAC signature of the JSON-encoded payload that your application receives with the shared secret as the key, and checking its value against the signature that Dwolla generated and supplied in the X-Request-Signature-SHA-256 header.

const crypto = require("crypto");

const isSignatureValue = (body, signature) =>
  signature ===
    .createHmac("sha256", process.env.DWOLLA_WEBHOOK_SECRET)

Considerations and Limitations #

When implementing webhook authentication in your application, please consider the following:

  • Dwolla uses a highly dynamic and wide range of IP addresses, meaning that your application cannot use IP whitelisting to authenticate webhook requests.
  • The request body is already JSON-encoded prior to being sent. As such, the JSON body should never be re-encoded! If it is, the signatures will not match and authentication will fail, even if the request came from Dwolla.
  • Your webhook endpoint must have access to the request body in order to authenticate the request. This means that some services, such as AWS Lambda Authorizers, are unable to authenticate a webhook Dwolla.

Queueing #

Once you ingest the webhook and authenticate that it is from Dwolla, instead of handling the business logic within the listener, we recommend passing the webhook off to a queue to be picked up by another Lambda function. By doing this, your webhook listener takes on the minimum amount of responsibility necessary, enabling your application to scale quickly and easily based on the volume of concurrent requests.

Which messaging broker you use is up to you; however, we recommend using AWS SQS or GCP Pub/Sub if your application resides in a serverless environment. If your application is self-hosted, Redis is also a good alternative to the options listed above.

As an example, the following snippet will demonstrate how a webhook could be placed on an AWS SQS queue. In it QueueUrl would be the URL of your SQS queue, whereas MessageBody would be the “stringified” body of the webhook that Dwolla sends. (For more information on sending a message using AWS SQS, we recommend checking out their official documentation.)

const { SQS } = require("aws-sdk");
const sqs = new SQS();

try {
  await sqs
      QueueUrl: "AWS_SQS_URL",
      MessageBody: JSON.stringify("WEBHOOK_JSON_BODY"),
} catch (e) {
  console.error("Failed to place webhook in SQS queue", e);

Processing #

Now that the webhook has been ingested, authenticated, and queued, once the webhook has been fetched from the queue, we recommend checking for duplicate events and then handling any application-specific business logic.

Check for Duplicate Events #

Although Dwolla tries to only send a webhook once, it is possible that the same webhook may be sent more than once. Because of this, it is recommended to check an internal database against the webhook’s Event ID to ensure that the same business logic is not processed multiple times.

In other words, we recommend maintaining an internal database that keeps track of all of the events that are processed. Then, when a new webhook is received, your application ensures that the event (checked against its ID) has not already been processed previously. Additionally, once your application finishes processing an event, its ID is appended to your database, ensuring that if your application receives another webhook with the same event ID, it will not get processed a second time.

Handle Business Logic #

Once you reach this point, you’re finally ready to handle any business logic related to the webhook — for example, creating or updating a database entry, sending a notification email or triggering an alert.

Although business logic will vary case by case based on your application’s specific needs, in this final section, we will demonstrate how you can use an AWS Lambda function to pull the webhook off an SQS queue, and print it to the console.

module.exports.queueHandler = async (event) => {
  try {
    event.Records.forEach((record) => {
      const webhook = JSON.parse(record.body);
        `Received ${webhook.topic}, body=${JSON.stringify(webhook, null, 2)}`
  } catch (e) {
      "An unexpected error occurred while processing the queue.",

Conclusion #

We hope that this guide is helpful in getting your webhook listener/handler set up and configured for use with Dwolla! If you would like to take a closer look at the code that we used (or give it a try using your own account), check out our webhook-receiver repository on GitHub.

Test in the Sandbox for free today.
Use sandbox environment to test API requests.
Get API Keys
2024 All Rights Reserved
Financial institutions play an important role in our network.

All funds transfers made using the Dwolla Platform are performed by a financial institution partner, and any funds held in a Dwolla Balance are held by a financial institution partner. Learn more about our financial institution partners.