Manage custom fields

The QuickBooks Online API provides up to three custom fields for the following transaction types:

Sales Transactions Expense Transactions
  • Invoice
  • Sales Receipt
  • Estimate
  • Credit Memo
  • Refund Receipt
  • Purchase Order

The custom fields could be business-specific fields such as a name, color, or size information. In this tutorial, we’ll focus on custom fields for QuickBooks Online Simple Start, Plus, and Essentials, and go through the different operations supported by the API. With QuickBooks Online Advanced, customers can create up to ten custom fields with various data types; however only the first three String custom fields will be available through the existing API functions outlined here. For this tutorial, you must use a QuickBooks Online sandbox company. Manage your sandbox companies here.

Enable custom fields

To use custom fields in our transactions, the first thing we need to do is enable them via the Company Settings page in our sandbox company.

qbo/docs/develop/tutorials/customfields1.png
qbo/docs/develop/tutorials/customfields1.png

x

qbo/docs/develop/tutorials/customfields2.png
qbo/docs/develop/tutorials/customfields2.png

x

That’s it! We’ve enabled custom fields and created a few fields for our sales transactions and a few for our expense transactions. If we’re creating an invoice in the product, this is where we’d see our custom fields:

qbo/docs/develop/tutorials/customfields3.png
qbo/docs/develop/tutorials/customfields3.png

x

Let’s take a detailed look at the operations supported, and how we’d go about performing those operations via the API Explorer and some code. We have SDKs for Java, .NET, and PHP. This tutorial includes code snippets in these languages for some of the operations.

Supported operations

Operation UI API
Create custom field names Yes No
Read custom field names Yes Yes
Create/update custom field values on a transaction Yes Yes
Delete custom field values on a transaction Yes Yes
Read custom field values on a transaction Yes Yes

Create custom fields

We cannot enable, create, modify, or delete custom field names via the QuickBooks Online API. The only way to do this is via the product UI itself, as we saw in the sections above.

Read custom fields

The QuickBooks Online API Preferences object contains the current custom field configuration.

There are two CustomField arrays inside SalesFormPrefs.CustomField and VendorAndPurchasePrefs.POCustomField. One array contains boolean values indicating if any of the three possible custom fields are enabled. The other array contains string values indicating the name of each custom field if the corresponding field is enabled. Field definitions may not appear in numeric order in the Preferences response body.

  Sales Transactions Expense Transactions
Determine if custom field is enabled SalesFormsPrefs.CustomField.CustomField where Name is “SalesFormsPrefs.UseSalesCustom#” (# indicates the position in which the field appears in the UI, 1-3). The boolean value will indicate if the custom field is enabled. VendorAndPurchasePrefs.POCustomField.CustomField where Name is “PurchasePrefs.UsePurchaseCustom#” (# indicates the position in which the field appears in the UI, 1-3). The boolean value will indicate if the custom field is enabled.
Determine custom field name SalesFormsPrefs.CustomField.CustomField where Name starts with “SalesFormsPrefs.SalesCustomName#” (# indicates the position in which the field appears in the UI, 1-3). VendorAndPurchasePrefs.POCustomField.CustomField where Name starts with “PurchasePrefs.PurchaseCustomName#” (# indicates the position in which the field appears in the UI, 1-3).

What does this object look like? Let’s check in the API Explorer.

  1. Visit the API Explorer.
  2. We need to sign in to enable testing with our sandbox company. Click ‘Sign In’ in the upper-right. Once you sign in, select your company from the dropdown on top.
  3. On the right, we can see the Request URL and the query select * from Preferences
  4. Now we can click the ‘Try It’ button to GET the request from our API. The response may look something like this (note that non-relevant fields have been omitted for clarity):

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
{
   "QueryResponse":{
      "Preferences":[
         {
            "SalesFormsPrefs":{
               "CustomField":[
                  {
                     "CustomField":[
                        {
                           "Name":"SalesFormsPrefs.UseSalesCustom3",
                           "Type":"BooleanType",
                           "BooleanValue":false
                        },
                        {
                           "Name":"SalesFormsPrefs.UseSalesCustom1",
                           "Type":"BooleanType",
                           "BooleanValue":true
                        },
                        {
                           "Name":"SalesFormsPrefs.UseSalesCustom2",
                           "Type":"BooleanType",
                           "BooleanValue":true
                        }
                     ]
                  },
                  {
                     "CustomField":[
                        {
                           "Name":"SalesFormsPrefs.SalesCustomName2",
                           "Type":"StringType",
                           "StringValue":"Field Two"
                        },
                        {
                           "Name":"SalesFormsPrefs.SalesCustomName1",
                           "Type":"StringType",
                           "StringValue":"Field One"
                        }
                     ]
                  }
               ]
            },
            "VendorAndPurchasesPrefs":{
               "POCustomField":[
                  {
                     "CustomField":[
                        {
                           "Name":"PurchasePrefs.UsePurchaseCustom1",
                           "Type":"BooleanType",
                           "BooleanValue":true
                        },
                        {
                           "Name":"PurchasePrefs.UsePurchaseCustom2",
                           "Type":"BooleanType",
                           "BooleanValue":false
                        },
                        {
                           "Name":"PurchasePrefs.UsePurchaseCustom3",
                           "Type":"BooleanType",
                           "BooleanValue":false
                        }
                     ]
                  },
                  {
                     "CustomField":[
                        {
                           "Name":"PurchasePrefs.PurchaseCustomName1",
                           "Type":"StringType",
                           "StringValue":"Field One"
                        }
                     ]
                  }
               ]
            },
            "AccountingInfoPrefs":{ },
            "ProductAndServicesPrefs":{  },
            "EmailMessagesPrefs":{ },
            "TimeTrackingPrefs":{ },
            "TaxPrefs":{  },
            "CurrencyPrefs":{ },
            "ReportPrefs":{ },
            "OtherPrefs":{ },
            "domain":"QBO",
            "sparse":false,
            "Id":"1",
            "SyncToken":"2"
         }
      ]
   }
}

This response matches the custom fields that we set up in an earlier section. If we look at the first array under SalesFormsPrefs.CustomField, we see that UseSalesCustom1 and UseSalesCustom2 are set to true. That’s because we only created two custom fields in the product under the ‘Sales’ tab. If we look at the second array under SalesFormsPrefs.CustomField, we see the names of the two custom fields we created, i.e. “Field One” and “Field Two”. The arrays under VendorAndPurchasesPrefs.POCustomField show a similar picture for a custom field that was enabled and named under the ‘Expenses’ tab.

Now how do we do this in code?

.NET

Java

PHP

 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
//create oauth object
OAuth2RequestValidator oauthValidator = new OAuth2RequestValidator(accessToken);

//create context
ServiceContext serviceContext = new ServiceContext(realmId], IntuitServicesType.QBO, oauthValidator);

//create DataService
DataService commonServiceQBO = new DataService(serviceContext);

// get all preferences
QueryService < Preferences > prefService = new QueryService < Preferences > (serviceContext);
Preferences pref = prefService.ExecuteIdsQuery("SELECT * FROM Preferences").First();

List < CustomFieldDefinition > customFieldDefinitions = pref.SalesFormsPrefs.CustomField.ToList();
//To retrieve PO custom field definition list use //pref.VendorAndPurchasesPrefs.POCustomField.ToList();

//If size is 2, indicate CustomerField is enabled. The first CustomerField defintion is structure, //the second one is the actual value.
if (customFieldDefinitions.Count() == 2) {

 //loop through the 2nd list to get the name and value of custom field
 List < CustomField > customFields = customFieldDefinitions[0].CustomField.ToList();

 if (customFields.Count() > 0) {

  //Note - retrieving data only for one custom field for illustration, loop through the list to get data for others
  String customFieldName = pref.SalesFormsPrefs.CustomField[0].CustomField[0].Name;

  string customFieldDefinitionId = pref.SalesFormsPrefs.CustomField[0].CustomField[0].Name.Substring(pref.SalesFormsPrefs.CustomField[0].CustomField[0].Name.Length - 1);


  string customFieldValue = pref.SalesFormsPrefs.CustomField[0].CustomField[0].Name;

 }

 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
37
38
//create oauth object
OAuth2Authorizer oauth = new OAuth2Authorizer(accessToken);

//create context
Context context = new Context(oauth, ServiceType.QBO, realmId);

//create DataService
DataService service = new DataService(context);
// get all preferences
String sql = "select * from preferences";
QueryResult queryResult = service.executeQuery(sql);
String definitionId = null;
if (!queryResult.getEntities().isEmpty() && queryResult.getEntities().size() > 0) {
    Preferences preferences = (Preferences) queryResult.getEntities().get(0);

    //get custom field definition list
    List < CustomFieldDefinition > customFieldDefinitions = preferences.getSalesFormsPrefs().getCustomField();
    System.out.println("customFieldDefinitions size: " + customFieldDefinitions.size());
    //To retrieve PO custom field definition list use //preferences.getVendorAndPurchasesPrefs().getPOCustomField();

    //If size is 2, indicate CustomerField is enabled. The first CustomerField defintion is structure, //the second one is the actual value.
    if (customFieldDefinitions.size() == 2) {

        //loop through the 2nd list to get the name and value of custom field
        List < CustomField > customFields = customFieldDefinitions.get(1).getCustomField();
        System.out.println("customFields size: " + customFields.size());

        if (customFields.size() > 0) {

            //Note - retrieving data only for one custom field for illustration, loop through the list to get data for others
            CustomField customField = customFields.get(0);
            System.out.println("customField name: " + customField.getName());
            definitionId = customField.getName().substring(customField.getName().length() - 1);
            System.out.println("definition id: " + definitionId);
            System.out.println("customField value: " + customField.getStringValue());
        }
    }
}

 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
// Prep Data Services
$dataService = DataService::Configure(array(
  'auth_mode' => 'oauth2',
  'ClientID' => "ClientID",
  'ClientSecret' => "ClientSecret",
  'accessTokenKey' =>  'accessToken',
  'refreshTokenKey' => "refreshToken",
  'QBORealmID' => "RealmID",
  'baseUrl' => "development"
));

//Get company preferences
$companyPreference = $dataService->getCompanyPreferences();

//Get CustomeField settings
$customerField = $companyPreference->SalesFormsPrefs->CustomField;

//To retrieve PO custom field definition list use //$companyPreference->VendorAndPurchasesPrefs>POCustomField;


//If size is 2, indicate CustomerField is enabled. The first CustomerField defintion is structure, //the second one is the actual value.
if(sizeof($customerField) == 2){
   echo "CustomerField is enabled.\n";
}

//Get the actual customerField
$actualCustomerField = end($customerField)->CustomField;

//Find the name of each field
foreach($actualCustomerField as $field){
  $name = $field->StringValue;
  $definationId = substr($field->Name, -1);
  echo "Field name is: " . $name . " and the defination ID for the field is: " . $definationId . "\n";
}

//For purchase order, you can use similar methods to find out the name of each field

Create/update custom field values on a transaction

We’ve created and enabled custom fields on the product, and we’ve learned how to read the field names using the API. Our next step is to create or update custom field values on transactions. All operations include:

The image below shows how these fields from Preferences correspond to a custom field element in an Invoice object.

qbo/docs/develop/tutorials/customfields-mapping.png
qbo/docs/develop/tutorials/customfields-mapping.png

x

Let’s proceed with an example. We’ve set up our company so that it allows a custom field called ‘Field One’ on sales transactions. Let’s create an invoice and set the value of ‘Field One’ to ‘my custom value’.

  1. Head back to API Explorer, sign in and select your sandbox company.
  2. If you’re not there already, navigate to Invoice -> Create an invoice
  3. Let’s modify the default Request Body and add data for the custom field. Add the JSON request below and click the Try It button to POST to our API.

 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
{
  "Line": [
    {
      "DetailType": "SalesItemLineDetail",
      "Amount": 100.0,
      "SalesItemLineDetail": {
        "ItemRef": {
          "name": "Services",
          "value": "1"
        }
      }
    }
  ],
  "CustomField": [
      {
        "DefinitionId": "1",
        "StringValue": "my custom value",
        "Type": "StringType",
        "Name": "Field One"
      }
    ],
  "CustomerRef": {
    "value": "1"
  }
}

We’ve created a simple invoice now, and the invoice contains the custom field ‘Field One’ whose value is ‘my custom value’. This is what the invoice might look like in the product UI.

qbo/docs/develop/tutorials/customfields-invoice.png
qbo/docs/develop/tutorials/customfields-invoice.png

x

Here’s how we would do this in code:

.NET

Java

PHP

 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
//create oauth object
OAuth2RequestValidator oauthValidator = new OAuth2RequestValidator(accessToken);

//create context
ServiceContext serviceContext = new ServiceContext(realmId], IntuitServicesType.QBO, oauthValidator);

//create DataService
DataService commonServiceQBO = new DataService(serviceContext);

//Create Invoice
Invoice invoiceObj = new Invoice();


//Add custom fields to invoice object apart from other properties
//more code here

List < CustomField > culist = new List < CustomField > ();
CustomField c1 = new CustomField();
c1.DefinitionId = "1";
c1.Name = "Field One";
c1.Type = CustomFieldTypeEnum.StringType;
c1.AnyIntuitObject = "my custom value";

culist.Add(c1);
invoiceObj.CustomField = culist.ToArray();

Invoice reInvoice1 = commonServiceQBO.Add(invoiceObj);

 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
37
38
39
40
//create invoice using custom fields
Invoice invoice = new Invoice();
//set custom fields
List < CustomField > invoiceCustomFields = new ArrayList < > ();
CustomField customField = new CustomField();
customField.setDefinitionId(definitionId);
customField.setType(CustomFieldTypeEnum.STRING_TYPE);
customField.setStringValue("my custom value");
invoice.setCustomField(invoiceCustomFields);

//set other fields
Customer customer = null;
ReferenceType customerRef = new ReferenceType();
List < Customer > customers = (List < Customer > ) service.findAll(new Customer());
if (!customers.isEmpty()) {
    customer = customers.get(0);
    customerRef.setName(customer.getDisplayName());
    customerRef.setValue(customer.getId());
}
invoice.setCustomerRef(customerRef);
Line line1 = new Line();
line1.setAmount(new BigDecimal("300.00"));
line1.setDetailType(LineDetailTypeEnum.SALES_ITEM_LINE_DETAIL);
SalesItemLineDetail salesItemLineDetail1 = new SalesItemLineDetail();
Item item = null;
ReferenceType itemRef = new ReferenceType();
List < Item > items = (List < Item > ) service.findAll(new Item());
if (!items.isEmpty()) {
    item = items.get(0);
    itemRef.setName(item.getName());
    itemRef.setValue(item.getId());
}
salesItemLineDetail1.setItemRef(itemRef);
line1.setSalesItemLineDetail(salesItemLineDetail1);
List < Line > lines1 = new ArrayList < > ();
lines1.add(line1);
invoice.setLine(lines1);

//add invoice
Invoice savedInvoice = service.add(invoice);

 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
37
38
// Prep Data Services
$dataService = DataService::Configure(array(
  'auth_mode' => 'oauth2',
  'ClientID' => "ClientID",
  'ClientSecret' => "ClientSecret",
  'accessTokenKey' =>  'accessToken',
  'refreshTokenKey' => "refreshToken",
  'QBORealmID' => "RealmID",
  'baseUrl' => "development"
));

//Create the invoice with CustomField
$theResourceObj = Invoice::create([
     "Line" => [
   [
     "Amount" => 100.00,
     "DetailType" => "SalesItemLineDetail",
     "SalesItemLineDetail" => [
       "ItemRef" => [
         "value" => 62,
         "name" => "Hours"
        ]
      ]
      ]
    ],
   "CustomerRef"=> [
        "value"=> 85
    ],
    "CustomField" => [
      [
        "DefinitionId" => "1",
        "Name" => "Field One",
        "Type" => "StringType",
        "StringValue" => "my custom value"
      ]
    ]
]);
$resultingObj = $dataService->Add($theResourceObj);

Delete custom field values on a transaction

To delete the custom field value on a transaction, follow the pattern to update a custom field, and set the CustomField.StringValue to the empty string “”.

Read custom field values on a transaction

If a transaction contains custom field values, they will be returned in the transaction response object. Continuing with our example above, let’s now read that Invoice and see how the custom field values are set.

  1. Head back to API Explorer, sign in and select your sandbox company.
  2. If you’re not there already, navigate to Invoice -> Read an invoice
  3. Enter the ID of the Invoice previously created and click the Try It button to GET from our API. The response may look something like this (note that non-relevant fields have been omitted for clarity):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    ...
    "CustomField": [
      {
        "DefinitionId": "1",
        "Name": "Field One",
        "Type": "StringType",
        "StringValue": "my custom value"
      }
    ],
    ...
}

Now that we understand how to read the array and what each field means, it’s simple! Translating this request to code is easy as well. Take a look:

.NET

Java

PHP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//create oauth object
OAuth2RequestValidator oauthValidator = new OAuth2RequestValidator(accessToken);

//create context
ServiceContext serviceContext = new ServiceContext(realmId], IntuitServicesType.QBO, oauthValidator);

//create DataService
DataService commonServiceQBO = new DataService(serviceContext);


//Use QueryService to read an existing Invoice having custom fields
QueryService<Invoice> querservice = new QueryService<Invoice>(context);
Invoice invoiceObj = queryservice.ExecuteIdsQuery("select * from Invoice where Id='1663'").FirstOrDefault();

//get customFieldName
var customFieldName =  invoiceObj.CustomField[0].Name;
//get custom field definitionId
var customFieldDefinitionId = invoiceObj.CustomField[0].DefintionId;
//get custom field value
var customFieldValue = invoiceObj.CustomField[0].AnyIntuitObject;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//Read custom field value from Invoice
Invoice readInvoice = service.findById(savedInvoice);
List < CustomField > readCustomFields = readInvoice.getCustomField();
for (CustomField readCustomField: readCustomFields) {

    System.out.println("customField definition id: " + readCustomField.getDefinitionId());
    System.out.println("customField name: " + readCustomField.getName());
    System.out.println("customField type: " + readCustomField.getType());
    System.out.println("customField value: " + readCustomField.getStringValue());
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Prep Data Services
$dataService = DataService::Configure(array(
  'auth_mode' => 'oauth2',
  'ClientID' => "ClientID",
  'ClientSecret' => "ClientSecret",
  'accessTokenKey' =>  'accessToken',
  'refreshTokenKey' => "refreshToken",
  'QBORealmID' => "RealmID",
  'baseUrl' => "development"
));

//Read the Invoice and find the CustomField
$invoice = $dataService->FindById("invoice", 292);
$customerField = $invoice->CustomField;
if(is_array($customerField)){
   foreach($customerField as $aCustomField){
     $definitionID = $aCustomField->DefinitionId;
     $name = $aCustomField->Name;
     $stringValue = $aCustomField->StringValue;
     echo " The value is $definitionID $name $stringValue\n";
   }
}

That completes our tutorial on custom fields. We’ve looked at how to enable them in the product and how to manage them via the API. Custom fields can be very powerful to add meaningful data to a sales or expense transaction and the API allows us to get and set this data with ease.

Custom fields for QuickBooks Online Advanced

As QuickBooks Online Advanced allows creation of up to ten custom fields with various data types such as string, date, numeric, lists and the API allows for only three String based fields, your app will only be able to support syncing data for three string custom fields. You will not be able to retrieve information for the rest of the custom fields at this time. This is done to retain compatibility with all versions of QuickBooks Online.

The detailed mapping between the new custom fields and the API response is explained in our blog here.