Webhooks

Use webhooks to receive event triggered callbacks for entities that your app needs to stay on top of. Webhooks automatically notify you whenever data changes in your end-user’s QuickBooks Online company files.

Webhooks apply to all QuickBooks Online companies connected to your app.

You’ll need to configure an endpoint our servers can call whenever user data changes trigger notifications. Once webhooks are active, we’ll send the requested event data, changes, and notifications.

Step 1: Set up OAuth 2.0

If you haven’t already, set up OAuth 2.0 for your app.

Even if webhooks are active, you’ll only receive change notifications for QuickBooks Online companies that are connected and authorized via OAuth 2.0.

Tip: If you plan to test webhooks for sandbox environments, the QuickBooks Online companies you test with need to complete the authentication flow via OAuth 2.0.
Step 2: See example implementations

Here are a few example webhook implementations:

Step 3: Review supported API entities

See which entities and operations support webhooks

This table shows the entities that support webhooks and the permitted operations.

  Create Update Delete Merge Void
Account  
Bill    
BillPayment  
Budget      
Class    
CreditMemo  
Currency      
Customer  
Department    
Deposit    
Employee  
Estimate    
Invoice  
Item  
JournalCode      
JournalEntry    
Payment  
PaymentMethod    
Preferences        
Purchase  
PurchaseOrder    
RefundReceipt  
SalesReceipt  
TaxAgency      
Term      
TimeActivity    
Transfer  
Vendor  
VendorCredit    
Step 4: Configure webhook endpoints for an app
  1. Sign in to your developer account.
  2. Select My Hub > App dashboard from the upper-right corner of the toolbar.
  3. Select and open the app you want to subscribe.

There are two sets of webhooks: one for live, in-production apps and a separate set for sandbox and testing environments.

You need to set up webhooks for production apps and sandbox environments separately.

Note: Webhook endpoint URLs must be exposed over the internet and secured via HTTPS. Make sure the specified domain has intermediate certificates installed to complete the chain of trust. Self-signed certificates aren’t supported.

For live, in-production apps
  1. From the left navigation pane, select Webhooks.
  2. Select Production.
  3. Enter the Endpoint URL. This is where the server will send notifications.
  4. Select the Show webhooks dropdown.
  5. Review the events and operations.
  6. Select the notifications and actions you want to enable.
  7. Select Save.
Note: The event aggregate feature configurable on the subscription page is deprecated and will be removed soon.

For sandbox and developer environments
  1. From the left navigation pane, select Webhooks.
  2. Select Development.
  3. Enter the Endpoint URL. This is where the server will send notifications.
  4. Select the Show webhooks dropdown.
  5. Review the events and operations.
  6. Select the notifications and actions you want to enable.
  7. Select Save.
Tip: It may take up to five minutes to get your first webhook notification.
Step 5: Validate webhooks notifications

After you configure webhook endpoints, we provide an app-specific verifier token. Use verifier tokens to validate the webhook notifications from the callback are from Intuit.

To see verifier tokens:

  1. Sign in to your developer account.
  2. Select My Hub > App dashboard from the upper-right corner of the toolbar.
  3. Select and open the app you want to verify.
  4. From the left navigation pane, select Webhooks.
  5. Select Development or Production.
  6. Review the Verifier Token field.

To use verifier tokens:

  1. Hash the notification payload with HMAC_SHA256_ALGORITHM using the <verifier token> value as the key.
  2. Compare the payload hash value with the intuit-signature header from the notification. The values should be identical.

Here’s a sample header:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

Here’s a sample signature verification in Java:

 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
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;
      }
   }
}
Step 6: Review webhooks notifications

Webhook notifications are POSTs with a JSON body. The response body contains the following fields:

Field Description
name The name of the entity that changed (customer, Invoice, etc.)
id The ID of the changed entity
operation The type of change
lastUpdated The latest timestamp in UTC
deletedID The ID of the deleted or merged entity (this only applies to merge events)

Here’s an example server response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
   "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"
         }]
      }
   }]
}

Webhook events are composed of an array of individual event notifications. Each of these event notifications correspond to unique information associated with a specific realm ID. Importantly, the realm ID includes multiple entities, representing various types of data within that realm ID.

Using the above example, if your app is connected to multiple companies, you will receive an array of event notifications. Each element in this array represents a unique update for a specific company.

Each event notification can include multiple entities such as Customer or Vendor. These entities within the same realm ID denote changes or operations, such as Create, that happened at the given lastUpdated time.

Tip: There are no Intuit-imposed limits to payload size or number of events. Individual server architectures may impose their own limits (2MB is a common default size limit). Assume this limit is imposed by your server unless you know otherwise.
Use webhook best practices

Reliability: To compensate for the possibility of missed events, make a ChangeDataCapture (CDC) call for all required entities, dating back to the last known successfully processed webhook event for each entity.

Additionally, you can make a daily CDC call for all required entities to ensure your app has always processed the most up-to-date data.

You can see this in the example webhook implementations.

Respond promptly: If your endpoint doesn’t respond within three seconds, the transaction will time out and retry.

Make sure your app can always respond quickly. Don’t process the notification payload or perform complex operations within the webhooks endpoint implementation. Do the processing on a separate thread asynchronously using a queue.

You can see this in the example webhook implementations.

Manage concurrency: Event notifications are sent one realm ID at a time. When there are multiple rapid changes, your app may get frequent notifications. Process the queue linearly to avoid processing the same changes more than once.

Notification ordering: It’s possible to receive events out of sequence. The timestamp field in the notification payload is always the source of truth for when events occur.

Retry policy: If the endpoint URL specified in your app configuration is down, we’ll retry progressively at intervals of 20, 30, and 50 minutes.

If it’s still down after that, we’ll drop the message and blacklist the endpoint. The endpoint will become inactive after one day. The retry service is only triggered for the following status codes: 500, 502, 503, 504, 408.