Apex Integration: Salesforce to Xero
Table Of Content
2
3
Overview
Integrating the Salesforce and Xero, for automation in business operations tasks is essential. This integration facilitates the seamless management of Xero Customers and Invoices within Salesforce. While an off-the-shelf Xero Salesforce Integration may not completely align with your specific business requirements, customizing Apex code offers the flexibility to develop a tailored solution. This post is designed to walk you through the steps necessary to achieve this integration.
The requirements for integrating are: you’ll need admin access to Xero and Salesforce (Enterprise Edition or higher, or a Dev/Sandbox org for testing). You’ll need some experience with Apex code. If you lack experience with Apex, you may want to consider using a no-code integration with Zapier or Breadwinner’s no-code integration between Salesforce and Xero.
Remote Site Settings
In order to enable a callout from Salesforce to an external website, it is essential for the target site to be registered in the Remote Site Settings. Calls to unregistered network addresses are restricted by Salesforce; thus, if the site is not registered, the callout will not function.
To prepare for calling Xero Endpoints, it’s necessary to establish two remote site settings in Salesforce using the steps mentioned below:
- Go to Setup.
- IIn the Quick Find, search for “Remote Site.”
- Click on “Remote Site Settings.”
- Create the following two remote site settings, each serving a specific purpose.
- For authentication and obtaining the Access Token: URL: https://identity.xero.com/
- For performing operations on data, such as Contacts, Invoices, Bills, or Purchase Orders, use: URL: https://api.xero.com/
Connecting to Xero using OAuth 2.0 Custom Connection
To establish a Custom Connection App on Xero, follow these steps:
- Navigate to “https://developer.xero.com.”
- Access the “My Apps” section.
- Provide all required details during the application configuration.
- Upon completing the configuration, obtain your unique Client ID and Client Secret. It’s important to note that your specific Client ID and Secret will differ from the provided examples.
Client ID: 8459AA********************5C8E0  
Client Secret: 3hZBw*********************************895nPi
You can use the above-generated codes to obtain the Access Token by requesting the endpoint “https://identity.xero.com/connect/token.”
Please see the sample code provided below.
XeroOAuthController.cls
public class XeroOAuthController {
public static string getTokens(){
String clientId = '7654AAB8*********************25C8E0';
String clientSecret = '73FGHo***************************95nPi';
Blob headerTokens = Blob.valueOf(clientId + ':' + clientSecret);
String authorizationToken = 'Basic ' + EncodingUtil.base64Encode(headerValue);
String requestPayload = 'grant_type=client_credentials&scope=accounting.transactions accounting.contacts';
HttpRequest request = new HttpRequest();
request.setEndpoint('https://identity.xero.com/connect/token');
request.setMethod('POST');
request.setHeader('Authorization', authorizationToken);
request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
request.setBody(requestPayload);
Http http = new Http();
HTTPResponse response = http.send(req);
if(response.getStatusCode() != 200 && response.getBody() != NULL){
String resBody = response.getBody();
Map<String, Object> resMap = (Map<String, Object>)JSON.deserializeUntyped(resBody);
if(resMap.containsKey('access_token'))
return String.valueOf(resMap.get('access_token'));
}
return null;
}
}
Use the Access Token generated (which is valid for 1800 seconds) from the above class for any further requests.
Xero Contacts
Reading Contacts
Use the following code to retrieve Xero Contact(s) information from Xero. You can also access individual Xero Contact information by specifying the “IDs” parameter within the endpoint.
XeroContactController.cls
public class XeroContactController {
public static final String Xero_BASE_URL = 'https://api.xero.com/api.xro/2.0/';
public static String getXeroContacts(){
String acToken = XeroOAuthController.getTokens();
system.debug(acToken);
if(acToken != NULL){
HttpRequest request = new HttpRequest();
request.setEndpoint(Xero_BASE_URL+'Contacts');
request.setMethod('GET');
request.setHeader('Authorization', 'Bearer '+acToken);
system.debug(request);
Http http = new Http();
HTTPResponse response = http.send(req);
if(response.getStatusCode() == 200 && response.getBody() != NULL){
String xeroContactsResponse = response.getBody();
system.debug(xeroContactsResponse);
return xeroContactsResponse;
}
}
return null ;
}
}
You can retrieve individual Xero contacts with the provided code by including the “IDs” parameter, as shown in the following endpoint.
https://api.xero.com/api.xro/2.0/Contacts?IDs=220ddca8-3144-4085-9a88-2d72c5133734,88192a99-cbc5-4a66-bf1a-2f9fea2d36d0
Managing Xero Contacts – Creation and Update
When establishing a Xero Contact, it’s essential to include certain mandatory fields as mentioned by Xero API guidelines. Conversely, when altering a Xero Contact, ensuring the inclusion of the “ContactID” in the request body is crucial.
The given code is adaptable for both creating and updating a Xero Contact, depending on the “isCreate” parameter’s value. If “isCreate” is TRUE, the request method will be set as PUT; otherwise, it will be configured as POST.
XeroContactController.cls
public class XeroContactController {
public static final String Xero_BASE_URL = 'https://api.xero.com/api.xro/2.0/';
public static String createXeroContact(Boolean isCreate){
String acToken = XeroOAuthController.getTokens();
system.debug(acToken);
if(acToken != NULL){
String requestJSONBody = '{ "Contacts": [ { "Name": "Adam Jones", "EmailAddress": "adam@avengers.com", "Phones": [ { "PhoneType": "MOBILE", "PhoneNumber": "666-1234", "PhoneAreaCode": "516" } ], "PaymentTerms": { "Bills": { "Day": 1, "Type": "OFCURRENTMONTH" }, "Sales": { "Day": 13, "Type": "DAYSAFTERBILLMONTH" } } } ] }';
HttpRequest request = new HttpRequest();
request.setEndpoint(Xero_BASE_URL+'Contacts');
request.setMethod(isCreate ? 'PUT' : 'POST');
request.setHeader('Authorization', 'Bearer '+acToken);
request.setHeader('Content-Type', 'application/json');
request.setHeader('Accept', 'application/json');
request.setBody(requestJSONBody);
system.debug(requestJSONBody);
Http http = new Http();
HTTPResponse response = http.send(request);
if(response.getStatusCode() == 200 && response.getBody() != NULL){
String xeroContactsResponse = response.getBody();
system.debug(xeroContactsResponse);
return xeroContactsResponse;
}
}
return null ;
}
}
Xero Invoices
Reading Invoices
Using the code below you can retrieve Xero Invoice(s) information from Xero. You can also access any Xero Invoice information by specifying the “IDs” parameter within the endpoint.
XeroInvoiceController.cls
public class XeroInvoiceController {
public static final String Xero_BASE_URL = 'https://api.xero.com/api.xro/2.0/';
public static String getXeroInvoices(){
String acToken = XeroOAuthController.getTokens();
system.debug(acToken);
if(acToken != NULL){
HttpRequest request = new HttpRequest();
request.setEndpoint(Xero_BASE_URL+'Invoices');
request.setMethod('GET');
request.setHeader('Authorization', 'Bearer '+acToken);
system.debug(request);
Http http = new Http();
HTTPResponse response = http.send(request);
if(response.getStatusCode() == 200 && response.getBody() != NULL){
String xeroInvoicesResponse = response.getBody();
system.debug(xeroInvoicesResponse);
return xeroInvoicesResponse;
}
}
return null;
}
}
You can retrieve Xero Invoices with the provided code by including the “IDs” parameter.
https://api.xero.com/api.xro/2.0/Invoices?IDs=fee88eea-f2aa-4a71-a372-33d6d83d3c45,9f5bca33-8590-4b6f-acfb-e85712b10217
Managing Xero Invoices – Creation and Update
When generating a Xero Invoice, specific mandatory fields must be supplied in accordance with the Xero API regulations. Conversely, when modifying a Xero Invoice, it is vital to include the “InvoiceID” in the request body.
The provided code is adaptable for both creating and updating a Xero Invoice, contingent on the value of the “isCreate” parameter. If the “isCreate” parameter is set to TRUE, the request method will be configured as PUT; otherwise, it will be configured as POST.
XeroInvoiceController.cls
public class XeroInvoiceController {
public static final String Xero_BASE_URL = 'https://api.xero.com/api.xro/2.0/';
public static String modifyXeroInvoice(Boolean isCreate){
String acToken = XeroOAuthController.getTokens();
system.debug(acToken);
if(acToken != NULL){
String invoiceRequestBody = '{ "Invoices": [ { "Type": "ACCREC", "Contact": { "ContactID": "430fa14a-f945-44d3-9f97-5df5e28441b8" }, "LineItems": [ { "Description": "Zig Wheels", "Quantity": 5, "UnitAmount": 25, "AccountCode": "200", "TaxType": "NONE", "LineAmount": 125 } ], "Date": "2023-03-11", "DueDate": "2023-12-10", "Reference": "Design Reference", "Status": "AUTHORISED" } ] }';
HttpRequest request = new HttpRequest();
request.setEndpoint(Xero_BASE_URL+'Invoices');
request.setMethod(isCreate ? 'PUT' : 'POST');
request.setHeader('Authorization', 'Bearer '+acToken);
request.setHeader('Content-Type', 'application/json');
request.setHeader('Accept', 'application/json');
request.setBody(invoiceRequestBody);
system.debug(request);
Http http = new Http();
HTTPResponse response = http.send(request);
if(response.getStatusCode() == 200 && response.getBody() != NULL){
String xeroInvoicesResponse = response.getBody();
system.debug(xeroInvoicesResponse);
return xeroInvoicesResponse;
}
}
return null ;
}
}
Alternatives to a Custom Integration
If you find it challenging to write a custom integration with Apex because it lacks LWC components or cannot handle updates, race conditions, or other unusual issues, then consider using Breadwinner’s Xero Salesforce Integration. It has clearly defined costs and can help your business grow and scale quickly.
Alternatively, you can also integrate Salesforce and Xero by using Zapier or Workato.