Title:
Xero Contacts API only updates “Bills” payment terms but ignores “Sales” payment terms (no error returned)
Body:
I’m working on a Salesforce → Xero integration that updates contact payment terms using the Xero Contacts API (POST /api.xro/2.0/Contacts).
The payload I’m sending includes both Bills and Sales payment terms like this:
{
"Contacts": [
{
"ContactID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"PaymentTerms": {
"Bills": { "Day": 7, "Type": "DAYSAFTERBILLDATE" },
"Sales": { "Day": 7, "Type": "DAYSAFTERBILLDATE" }
}
}
]
}
Full Code:
public class XeroAPIHandler {
public class AccountInput {
@InvocableVariable(required=true)
public Id accountId;
}
@InvocableMethod(label='Update Xero Payment Terms' description='Updates Xero contact with payment terms based on Account field')
public static void updatePaymentTermsFromFlow(List inputList) {
for (AccountInput input : inputList) {
List acc = [SELECT Id, Name, Xero_Account_Number__c, Payment_Terms__c, Account_State__c
FROM Account
WHERE Id = :input.accountId
LIMIT 1];
if (acc.size() > 0) {
String tenantId = tenantIdForState(acc[0].Account_State__c);
String namedCredentialName = namedCredForState(acc[0].Account_State__c);
updatePaymentTerms(acc[0].Xero_Account_Number__c, acc[0].Payment_Terms__c, tenantId, namedCredentialName);
}
}
}
public static String tenantIdForState(String state) {
if (String.isBlank(state)) return null;
switch on state {
when 'ABC' { return Label.Xero_Tenant_Id_ABC; }
when 'DEF' { return Label.Xero_Tenant_Id_DEF; }
when 'GHI' { return Label.Xero_Tenant_Id_GHI; }
when 'JK' { return Label.Xero_Tenant_Id_JK; }
when else { return null; }
}
}
public static String namedCredForState(String state) {
if (String.isBlank(state)) return null;
switch on state {
when 'ABC' { return 'Xero_ABC'; }
when 'DEF' { return 'Xero_DEF'; }
when 'GHI' { return 'Xero_GHI'; }
when 'JK' { return 'Xero_JK'; }
when else { return null; }
}
}
@future(callout=true)
public static void updatePaymentTerms(String AccountNumber, String term, String tenantId, String namedCredentialName) {
String xeroContactId = AccountNumber;
if (String.isBlank(xeroContactId)) return;
XeroPaymentTermMapper.XeroPaymentTerm terms = XeroPaymentTermMapper.mapPaymentTerm(term);
if (terms == null) return;
Map payload = new Map{
'Contacts' => new List
The Problem:
The request succeeds with HTTP 200, and the contact’s Purchase defaults (Bills) are updated correctly in Xero. However, the Sales defaults are not being updated — they remain unchanged, and there’s no error or validation message in the response.
Has anyone successfully updated both Bills and Sales payment terms for a contact via the /api.xro/2.0/Contacts endpoint?