Webhooks

Webhooks are notifications about QuickBooks entities that are sent to developer-created applications. For example, if you wish to be notified when a customer's information changes, you configure a specific endpoint that QuickBooks can call for this event with the details of the change. When webhooks is active, data for the requested event is aggregated and then sent in a periodic notificiation to your API. Once you have configured webhooks, you will receive event notifications for all companies your app is connected to.

QuickBooks entities that support webhooks

This release provides support on the following entities:

Customer*InvoiceSalesReceipt
EstimateVendor*Account*
PaymentClassItem*
BillPaymentEmployee*Purchase
BillCreditMemoRefundReceipt
VendorCreditTimeActivity 
Note

Entities marked with an asterisk (*) support merge events.

Webhooks operations supported

Webhooks support the following operations:

  • Create
  • Update
  • Delete
  • Merge (only available for the entities marked with an asterisk above)

Configuring webhooks

You can enable webhooks on a per-app basis via the app settings menu.

  1. From your My Apps dashboard, click the app you wish to configure.
  2. Click Settings.
  3. Locate the Webhooks section and click Add webhook. The webhook configuration menu appears.
  4. Enter your endpoint URL in the field provided. This URL must be exposed over the internet and be secured via HTTPS.
  5. Check the events desired (or Select All to enable all of them). Each option can be expanded using the drop-down arrow on the right, allowing you to specify exactly which resource should trigger webhooks.
  6. Click Save. It may take up to five minutes for your endpoint to receive its first notification.

Once you have configured the webhooks endpoint, you can modify or delete it using the buttons shown at the far right.

Screen Shot 2016-07-06 at 10.37.00 AM.png

Note

When you are testing your webhooks with your app, you must configure webhooks in the sandbox environment. Your app must have gone through OAuth flow and authorization with a sandbox company before events will be received. Once you are ready to go to production, you must configure webhooks again on the production side.

Notification details

The endpoint you configure for webhooks will receive notifications for the entities you have configured. The notification is a POST request with a JSON body and contains the following fields:

  • name: The name of the entity type that changed (Customer, Invoice, etc.).
  • id: The changed entity's ID.
  • operation: The type of change experienced (listed in Webhooks operations supported, above).
  • lastUpdated: The latest timestamp in UTC.
  • deletedId (only for Merge events): The ID of the entity that was deleted and merged.

Sample payload

{
    "eventNotifications":[
    {
        "realmId":"1185883450",
        "dataChangeEvent":
        {
            "entities":[
            {
                "name":"Customer",
                "id":"1",
                "operation":"Create",
                "lastUpdated":"2015-10-05T14:42:19-0700"
            },
            {
                "name":"Vendor",
                "id":"1",
                "operation":"Create",
                "lastUpdated":"2015-10-05T14:42:19-0700"
            }]
        }
    }]
}
Note

Although the notification is designed to accommodate multiple RealmIDs, it only uses one at this time. If your app is connected to multiple companies, you will receive multiple eventNotifications (one per RealmID).

Validating the notification

Once you configure a webhooks endpoint, Intuit provides an app-specific Verifier Token that allows you to validate the authenticity of the notification received when Intuit calls back your webhooks endpoint. You can view this by clicking Show Token after webhooks creation.

Using the Verifier token

The verifier token allows you to ensure that the webhooks notification actually comes from Intuit. To do so:

  1. Hash the notification payload with HMAC_SHA256_ALGORITHM using <verifier token> as the key.
  2. Convert the intuit-signature header from base-64 to base-16.
  3. Compare the value from step 1 to the intuit-signature header from the notification. The values should be identical.

Sample Header

content-length:262
intuit-created-time:2016-02-02T16:25:00-0800
intuit-t-id:9cf50b60-8b0e-4fea-8327-e6a66099fe6f
proxy-connection:keep-alive
host:sample-endpoint.ilb.idg-notify-ppd.a.intuit.com:8443
intuit-notification-schema-version:0.1
content-type:application/json; charset=utf-8
intuit-signature:6kQBQtjwjupelRMwkyJsnpq80uhz2o+Rn92+m03GhKE=
accept:application/json
user-agent:intuit_notification_server/0.1

Sample signature verification (Java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;

public class VerifySignatureExample {
    private static final String SIGNATURE = "intuit-signature";
    private static final String ALGORITHM = "HmacSHA256";
    public boolean isRequestValid(Map<String, String> headers, String payload, String verifier) {
        String signature = headers.get(SIGNATURE);
        if (signature == null) {
            return false;
        }
        try {
            SecretKeySpec secretKey = new SecretKeySpec(verifier.getBytes("UTF-8"), ALGORITHM);
            Mac mac = Mac.getInstance(ALGORITHM);
            mac.init(secretKey);
            String hash = Base64.getEncoder().encodeToString(mac.doFinal(payload.getBytes()));
            return hash.equals(signature);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) {
            return false;
        }
    }
}

Best practices

  • Reliability: In order to compensate for the possibility of missed or dropped packets, make a ChangeDataCapture (CDC) call for each entity received up to the last notification time (as shown in sample app) upon receipt of new notification. You can additionally make a daily CDC call for all entities to ensure that your database is consistently up to date.
  • Respond promptly: Your endpoint should respond within three seconds; otherwise, the transaction will time out and be retried. To make sure you can always respond quickly, do not process the notification payload or perform complex operations within the webhooks endpoint implementation. It is a good idea to do the processing on a separate thread asynchronously (as shown in the sample apps listed below) using a queue.
  • Manage Concurrency: Event notifications are sent for each realmID at a time. When there are multiple changes happening rapidly, your app may receive notifications frequently. Care should be taken to process the queue linearly to avoid processing the same changes more than once.
  • Notification Ordering: While we make all effort to send the events in order, it is possible to receive an older event in a subsequent notification. The event timestamp field in the notification payload is always the source of truth for when an event has occurred.
  • Retry Policy: If the endpoint is down, we will retry progressively (first time after 20 minutes, then again after 30 minutes, and a third time after 50 minutes) and finally drop the message and blacklist the endpoint if it is still down. The endpoint will become inactive after one day.

Sample apps

Refer to the following links for sample webhooks implementations.

 Got Questions? Get Answers in our developer forums.