Webhooks inform your backend when there are changes to data in Abound.

Setting up webhooks only takes two steps:

  1. The first step is to contact our support to set up your webhook URL to subscribe to an event. Events represent changes to a resource within Abound. Abound supports webhooks for the following events:
  • documents.created
  • documents.updated
  • paymentMethods.created
  • taxes.updated
  • taxPayments.created
  • taxPayments.updated
  • users.updated
  1. The next step is to test your webhook connection to ensure you are receiving events. Below is an example of the webhook request payload that would be sent to your URL when the taxPayments.updated event occurs:
{
  "resource":"taxPayments",
  "resourceId":"taxPaymentId_7777",
  "resourceURL":"https://sandbox-api.withabound.com/v2/users/userId_8e01c68862ff080637075b7f0e2105bd7229f434/taxPayments/taxPaymentId_7777",
  "resourceStatus": "pending",
  "event":"taxPayments.updated",
  "userId":"userId_8e01c68862ff080637075b7f0e2105bd7229f434",
  "webhookId":"webhookId_7777777",
  "webhookURL":"https://webhook.site/87a85187-96bc-42eb-81bc-65fb66369c44",
  "webhookRequestId": "webhookRequestId_YYYYYYYYYYYY"
}

If an error occurs on the resource connected to the webhook, the webhook will also return a resourceMessage.

{
  "resource": "documents",
  "resourceId": "documentId_XXXXXXXXXXXXXXXXXXXXXX",
  "resourceURL": "https://sandbox-api.withabound.com/v2/users/userId_0aa8b15c0cce59aa766156e6328798c80512131a/documents/documentId_9a1ccd4967787ea24bbcc2e818f619697b518338",
  "resourceStatus": "error",
  "resourceMessage": "payerError",
  "event": "documents.updated",
  "userId": "userId_0aa8b15c0cce59aa766156e6328798c80512131a",
  "webhookId": "webhookId_XYZZYXYZZYXYZZY",
  "webhookURL": "https://webhook.site/e665ac70-4914-4d8b-ab48-fa2b47680b4a",
  "webhookRequestId": "webhookRequestId_YYYYYYYYYYYY"
}

Important considerations for webhook requests:

  1. Webhook requests may be sent out of chronological order in which the events took place.
  2. Multiple webhook requests may be attempted for a single event.
  3. HTTPS URLs are required for use in the production environment.

Validation

Abound includes a validation header with each webhook call by including the header value ["Abound-Signature"]. This allows you to verify that the events were sent by Abound and not a third party. The signature is a a hash-based message authentication code (HMAC) with SHA-256.

Below is an example of how to validate

const {
  createHmac
} = await import('crypto');

// Header containing webhook signature
const sigHeader = event.headers['Abound-Signature'];

// Raw body of received request
const rawBody = event.body;

const hmac = createHmac('sha256', 'YOUR_APP_SECRET');
hmac.update(event.body);
const sigGenerated = hmac.digest('hex');

// Returns true if match
return sigGenerated === sigHeader
import hmac
import hashlib

## Header containing webhook signature
sig_header = request.headers.get("Abound-Signature")
##  Raw body of received request
raw_body = request.get_data(parse_form_data=True)

digest = hmac.new(
  key="YOUR_APP_SECRET".encode("utf-8"),
  msg=raw_body.encode(),
  digestmod=hashlib.sha256,
).hexdigest()

## Returns true if match
return digest == sig_header
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    // Header containing webhook signature
  sig_header:= r.Header.Get("Abound-Signature")
  
    // Raw body of received request
  raw_body := r.Body
  
    hmacVal := hmac.New(sha256.New,[]byte("YOUR_API_KEY"))
    hmacVal.Write([]byte(raw_body))
    sigGenerated := hex.EncodeToString(hmacVal.Sum(nil))

  // Returns true if match
  fmt.Println( hmac.Equal([]byte(sigGenerated), []byte(sig_header)))

}
require 'openssl'

## Header containing webhook signature
sig_header = request.headers.get("Abound-Signature")
##  Raw body of received request
raw_body = request.body

hmac = OpenSSL::HMAC.hexdigest("SHA256", 'YOUR_API_KEY', raw_body)

## Returns true if match
return hmac == sig_header

Did this page help you?