Validating webhooks

Assume that your integration is an online marketplace, and that a customer just placed an order on your site. A few days after the customer initiated their payment, your application receives this webhook.

The topic field of an event holds a description of the event, which is similar the subject of an e-mail message. The webhook itself contains _links the the resource impacted by the event that can be used to retrieve more information about the webhook you have received.

NOTE: The event must be retrieved with a client_credentials granted access_token.

  "id": "2c311238-b9ef-4763-b1cb-03e1aa651227",
  "resourceId": "0089A051-9B79-E511-80DB-0AA34A9B2388",
  "topic": "transfer_completed",
  "timestamp": "2015-10-23T15:35:35.366Z",
  "_links": {
    "self": {
      "href": ""
    "account": {
      "href": ""
    "resource": {
      "href": ""

Step A. Authenticating the webhook request

Before we process any data from the webhook we’ll want to validate that the request really came from Dwolla and not someone pretending to be Dwolla. Dwolla signs each webhook request with the secret you passed in when you created the webhook subscription. The signature is contained in the X-Request-Signature header and is a SHA-1 HMAC hash of the request body with the key being your webhook secret.

You can validate the webhook by generating the same SHA-1 HMAC hash and comparing it to the signature sent with the payload.

def verify_signature(payload_body, request_signature)
  signature = OpenSSL::HMAC.hexdigest("sha1"),
  unless Rack::Utils.secure_compare(signature, request_signature)
    halt 500, "Signatures didn't match!"
not available
var verifyGatewaySignature = function(proposed_signature, webhook_secret, payload_body) {
  var crypto    = require('crypto')
  , secret      = 'API_SECRET_HERE'
  , text        = webhook_secret + payload_body
  , hash;

hash = crypto.createHmac('sha1', secret).update(text).digest('hex');

return proposed_signature === hash;
def verify_gateway_signature(proposed_signature, webhook_secret, payload_body):
  import hmac
  import hashlib

  signature =, webhook_secret, hashlib.sha1).hexdigest()

  return True if (signature == proposed_signature) else False
function verifyGatewaySignature($proposedSignature, $webhookSecret, $payloadBody) {
    $signature = hash_hmac("sha1", $webhookSecret, $payloadBody);
    return $signature == $proposedSignature;

Step B. Check for duplicate events

It is important to consider that multiple webhooks are fired for the same action on certain events. For example, multiple webhooks are fired for Transfer events, that is, two transfer_created events with different resource IDs (and, by extension, resource URLs) will be fired, one for each customer. To avoid doing any business logic twice, you would have to check if you have already received a webhook relating to the Transfer resource responsible for the event.

To do this, keep a queue of events in a database and check to see if an Event has the same self resource location in _links as another event. If not, process the logic for that event. To illustrate, this is how a developer would implement this using Ruby and the ActiveRecord ORM.

check_db = ActiveRecord::Base.connection.execute("SELECT * FROM EVENTS WHERE SELF = #{event[:_links][:self].to_s}")

# check_db will be an array of rows returned
unless check_db.length() == 0
    # do something
Financial institutions play an important role in the Dwolla network.

Dwolla, Inc. is an agent of Veridian Credit Union and Compass Bank and all funds associated with your account in the Dwolla network are held in pooled accounts at Veridian Credit Union and Compass Bank. These funds are not eligible for individual insurance, including FDIC insurance and may not be eligible for share insurance by the National Credit Union Share Insurance Fund. Dwolla, Inc. is the operator of a software platform that communicates user instructions for funds transfers to Veridian Credit Union and Compass Bank.