FAQ

Here are answers to common questions and topics related to the Payroll API.

REST vs GraphQL

If you’re familiar with our QuickBooks Online Accounting API (referred to as Accounting API for the remainder of this section), you may be used to the REST framework. In this section, we’ll go over some general differences between REST and GraphQL, and some differences between the Accounting API and the GraphQL API.

HTTP request type

In REST, you would do a GET request to retrieve data from the server and a POST, PATCH PUT or DELETE to modify data on the server.

In GraphQL, all requests are ``POST``s and the body of the request tells the server what needs to be done. All of the HTTP calls are made the same way and to the same resource but with different request bodies. To retrieve data, you would use a query operation, and to modify data you would use a mutation operation. More on those operations here.

Single resource vs multiple resources

Let’s say you wanted to read an invoice and find out how that invoice was paid. In REST, these would typically be separate endpoints or entities called Invoice and Payment, which is what the Accounting API does. You would have to make one call to the Invoice endpoint to get the invoice information. You would then find the related Payment ID and then make another call to Payment endpoint to find out more information about the payment transaction.

If you were to do this in GraphQL, you would be dealing with a single resource. Depending on the schema, you would make a call to one resource and ask for the Invoice fields you need and the fields on the related Payment.

Fetching data

The Accounting API would return all the information it has about an entity that is queried. A simple read or query operation would return all the fields associated with the entity for the record that has been requested.

The GraphQL API gives you more control over what data you want from the server. Whether you are doing a query or a mutation, you can specify exactly which fields (and nested fields) you want from the server.

Identifiers

In the Accounting API, when you query for records, each record comes with an ID, which is unique only within that entity. So you may have an Invoice with ID 123 and a Payment with ID 123.

The GraphQL API has global IDs. Each entity returned in response has an ID that is unique across all entities.

Error response

The Accounting API returns HTTP status codes like 4** or 5** to indicate different types of errors.

The GraphQL API handles errors differently. Errors related to the API gateway later will return status codes 4** or 5**, but API errors will return HTTP status code 200 and provide all of the necessary information within the response body.

Operations

Let’s take a deeper dive into some common operations like create, read, update, delete, and query. We’ll take the example of the Accounting API’s Invoice entity for this section. An Invoice represents a sales form where the customer pays for a product or service later. It is associated with Items, Customers, and Payments. Although Invoice is not supported in the GraphQL API, we’ll do a comparison between how we work with this entity in the Accounting API vs how we would hypothetically do this in the GraphQL API.

In all of the snippets below you will see that in the Accounting API (REST), the response will be the object with all of the fields. In the GraphQL API, however, you can specify which fields you want and only those fields are a part of the response.

Note that these are illustrative examples only.

Create an invoice
REST

Request

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  POST <baseUrl>/v3/company/<realmID>/invoice

  {
    "Line": [
      {
        "DetailType": "SalesItemLineDetail",
        "Amount": 100.0,
        "SalesItemLineDetail": {
          "ItemRef": {
            "name": "Services",
            "value": "1"
          }
        }
      }
    ],
    "CustomerRef": {
      "value": "1"
    }
  }

Response

 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
28
29
30
31
32
33
34
35
36
  {
    "Invoice": {
      "Id": "238",
      "DocNumber": "1069",
      "Balance": 100.0,
      "TxnDate": "2020-07-24",
      "TotalAmt": 100.0,
      "CustomerRef": {
        "name": "Amy's Bird Sanctuary",
        "value": "1"
      },
      "ShipAddr": {
        "City": "Bayshore",
        "Line1": "4581 Finch St.",
        "PostalCode": "94326",
        "CountrySubDivisionCode": "CA",
        "Id": "109"
      },
      "DueDate": "2020-08-23",
      "Line": [
        {
          "Amount": 100.0,
          "SalesItemLineDetail": {
            "TaxCodeRef": {
              "value": "NON"
            },
            "ItemRef": {
              "name": "Services",
              "value": "1"
            }
          },
          "DetailType": "SalesItemLineDetail"
        }
      ],
          ... other fields ...
  }
GraphQL

Request

1
2
3
4
5
6
7
8
  POST <graphBaseURL>/v1

  mutation Create{
    createInvoice(item: {type: "inventory", amount: 100, itemref: "111"}, customerRef: "222", paymentRef: "333") {
      id
      docNumber
    }
  }

Response

1
2
3
4
  {
          "id": 1,
          "docNumber": "101"
  }
Read an invoice and get the payment type of the linked payment
REST

Request Example 1

1
  GET /v3/company/<realmID>/invoice/101

Response Example 1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  {
          "Invoice": {
                  "Id": "101",
                  "DocNumber": "1069",
                  "LinkedTxn": {
                          "TxnType": "Payment",
                          "TxnId": "801"
                  }
          ... other fields ...
  }

Request Example 2

1
  GET /v3/company/<realmID>/payment/801

Response Example 2

1
2
3
4
5
6
  {
          "Payment": {
                  "Id": "801",
                  "PaymentType": "CreditCard",
          ... other fields ...
  }
GraphQL

Request

1
2
3
4
5
6
7
8
9
  POST <graphBaseURL>/v1

  query getPaymentInfo  {
          invoices (id : 1) {
                  docNumber
                  payments {
                          paymentType
      }
  }

Response

1
2
3
4
5
6
  {
          docNumber: "101",
          payments{
                  paymentType: "Credit Card"
          }
  }
Update an existing invoice
REST

Request

1
2
3
4
5
6
7
8
  POST <baseUrl>/v3/company/<realmID>/invoice

  {
    "SyncToken": "0",
    "Id": "101",
    "sparse": true,
    "DueDate": "2020-09-30"
  }

Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  {
    "Invoice": {
      "Id": "101",
      "SyncToken": "1",
      "DocNumber": "1069",
      "DueDate": "2020-09-30",
      "Line": [
        {
  ... other fields ...
        }
      ],
          ... other fields ...
  }
GraphQL

Request

1
2
3
4
5
6
7
8
  POST <graphBaseURL>/v1

  mutation Update($id: ID!) {
    updateInvoice(id: $id, dueDate: "2020-09-30") {
      id
      dueDate
    }
  }

Response

1
2
3
4
  {
      id: 1,
      dueDate: "2020-09-30"
  }
Delete an invoice
REST

Request

1
2
3
4
5
6
  POST <baseUrl>/v3/company/<realmID>/invoice?operation=delete

  {
    "Id": "239",
    "SyncToken":"3"
  }

Response

1
2
3
4
5
6
7
  {
   "Invoice": {
    "domain": "QBO",
    "status": "Deleted",
    "Id": "239"
    }
  }
GraphQL

Request

1
2
3
4
5
6
7
  POST <graphBaseURL>/v1

      mutation Delete($id: ID!) {
        deleteInvoice(id: $id) {
          status
        }
      }

Response

1
2
3
  {
      status: "void"
  }
Query invoices

A common operation is to query invoices based on certain filters. Let’s say we want to get all invoices created within a date range.

REST

Request

1
  GET <baseUrl>/v3/company/<realmID>/query?query=select * from Invoice where TxnDate>'2020-01-18' and TxnDate<'2020-01-18'

Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  {
          "QueryResponse": {
                  "Invoice": [
                          {
                                  "Id": "201",
                                  ... other fields ...
                          },
                          {
                                  "Id": "202",
                                  ... other fields ...
                          },
                          ... more records ...
          ]
          }
  }
GraphQL

Request

1
2
3
4
5
6
7
  query getInvoices() {
    invoice(filter: {txnDate: {between: ['2020-01-18' , '2020-01-18' ]}}) {
      id
      status
      balance
    }
  }

Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  {
          "data": {
                  "invoice": [
                          {
                                  "id": "101",
                                  "status": "Paid",
                                  "balance": 0
                          },
                          {
                          ... more records ...
                  },
                  ... more records ...
          ]
          }
  }

The above are only illustrative examples. To get a full list of operations and resources available in the QuickBooks Payroll API, use the introspection query or our API reference docs. You can also refer to our collections for Insomnia and Postman here.

General payroll questions

The following are some commonly asked questions related to Payroll.

What do I need to access payroll data?

As of right now, the scopes necessary to access payroll data are not publicly exposed and cannot be onboarded without Intuit help. To get access you will need to work with an Intuit representative.

Should I be using development keys to test my app?

No, Currently the Payroll service does not have a sandbox/playground/dev environment to work with. As such, as a payroll 3rd-party partner you will be given a test account in the production environment you do not have to pay for, and as such you must use production keys.

Where are the employer contributions stored?

Within the payslip object per employee you will be able to see both the employee and employer contributions. This is set at the employee level, not generally at the company/employeer level to allow for different contribution levels of employees.

Do you expose payroll contribution types?

Not currently. We expose type for other elements within payroll but do not show a need for this information based on what our 3rd party partners are trying to accomplish.

Fix Intuit GraphQL API errors

If you encounter an error, the Intuit GraphQL API server returns an error message. These messages give you clues about the issue’s source and cause.

Step 1: Capture the intuit_tid value

Server responses for gateway and service errors have an intuit_tid field in the header. Capture this field’s value. It will help our support team quickly find and address reported issues.

Step 2: Identify the error type

When requests from apps go to our servers, they first pass through a gateway layer. Then they pass through the GraphQL Service layer. Thus, there are two general types: gateway errors and service errors.

Review the HTTP Status Code (xxx). This tells you the error type:

Step 3: Fix errors
Fix gateway errors

Gateway errors are generated when requests hit our server’s gateway layer. They apply to the whole request and follow the gateway error response format.

1
2
3
4
5
6
7
8
  HTTP Status Code 401
  {
    "code": "AuthenticationFailed",
    "type": "INPUT",
    "message": null,
    "detail": "Malformed bearer token: too short or too long",
    "moreInfo": null
  }

In the server response, review the following fields:

Many gateway errors are self-explanatory. Some require a bit more digging. For 403 errors, you may need to adjust code for user roles. For 401 errors, you may need to update your app’s access or refresh tokens.

SEE ALL GATEWAY ERRORS

Status code Error details
302 Validation Error
401 Resource redirect or resource has moved
403 Unauthenticated access: application authentication failed due to invalid or expired tokens
403 Resource not found: routing error, access or configuration on the Gateway, or incorrect resource requested
405 Method not allowed: attempt to request other than GET/POST requests
429 Too many requests: request is throttled as it exceeded the throttle policy.
500 Internal Server Error: missing POST body or other exceptions within application, or a service outage
502 Bad Gateway: Infrastructure misconfiguration, propagates response from downstream, or a service outage
503 Service unavailable: Outage
504 Service timeout: Outage
Fix service errors

Service errors are generated when requests hit our server’s service layer.

Service errors only apply to a subset of requests, not the entire request. Responses follow the standard GraphQL service format.

SEE ALL SERVICE ERRORS

Most service errors are in response to malformed queries, parsing problems, validation issues, or incorrect scopes. As a result, the server won’t authorize the request.

Here’s an example validation error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  "errors": [
      {
        "message":  "VAL-0001 Failed to fetch name for account with id#1",
        "locations": [ { "line": 6, "column": 7 } ],
        "path": [ "company", "account", 1, "name" ]
        "extensions": {
           "classification": "VALIDATION_ERROR"
        }
      }
  ]

Review the server response and use it as a guide. Each field gives clues for what failed, where, and how to fix it: