LoopFour
IntegrationsAccounting

Xero

Cloud accounting for invoices, payments, and bank reconciliation

Xero

Connect to Xero for cloud accounting automation including invoices, bills, payments, and bank transactions.

Overview

Xero is a cloud-based accounting platform. The integration supports:

  • Contacts - Customers and suppliers
  • Invoices - Sales invoices (AR)
  • Bills - Purchase invoices (AP)
  • Payments - Record payments against invoices
  • Bank Transactions - Spend and receive money
  • Accounts - Chart of accounts
  • Credit Notes - Customer and supplier credits
  • Items - Products and services
  • Reports - Financial reports

Prerequisites

  • Xero account (any plan)
  • Xero Developer account
  • OAuth app configured
  • Organisation (tenant) access

Authentication

Xero uses OAuth 2.0 for authentication via Nango.

curl "http://localhost:3000/api/v1/connections/xero/auth-url" \
  -H "x-api-key: YOUR_API_KEY"

Multi-Organization Support

Xero requires a tenant ID for all API calls. Get connected tenants first:

{
  "action": "xero.getConnections",
  "config": {}
}

Pass tenantId to all subsequent actions.

Available Actions

Connection Actions

getConnections

Get connected organizations (tenants).

{
  "action": "xero.getConnections",
  "config": {}
}

Contact Actions

getContact

Get contact by ID.

{
  "action": "xero.getContact",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "contactId": "{{input.contactId}}"
  }
}

createContact

Create a new contact.

{
  "id": "create-contact",
  "type": "action",
  "action": "xero.createContact",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "name": "{{input.companyName}}",
    "firstName": "{{input.firstName}}",
    "lastName": "{{input.lastName}}",
    "emailAddress": "{{input.email}}",
    "accountNumber": "{{input.accountNumber}}",
    "contactStatus": "ACTIVE",
    "isCustomer": true,
    "isSupplier": false,
    "defaultCurrency": "USD",
    "taxNumber": "{{input.taxId}}",
    "addresses": [
      {
        "AddressType": "POBOX",
        "AddressLine1": "{{input.address.street}}",
        "City": "{{input.address.city}}",
        "Region": "{{input.address.state}}",
        "PostalCode": "{{input.address.zip}}",
        "Country": "{{input.address.country}}"
      }
    ],
    "phones": [
      {
        "PhoneType": "DEFAULT",
        "PhoneNumber": "{{input.phone}}"
      }
    ],
    "website": "{{input.website}}",
    "paymentTerms": {
      "Sales": {
        "Day": 30,
        "Type": "DAYSAFTERBILLDATE"
      }
    }
  }
}

updateContact

Update an existing contact.

{
  "action": "xero.updateContact",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "contactId": "{{input.contactId}}",
    "emailAddress": "{{input.newEmail}}",
    "isCustomer": true
  }
}

archiveContact

Archive a contact (soft delete).

{
  "action": "xero.archiveContact",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "contactId": "{{input.contactId}}"
  }
}

listContacts

List contacts with filters.

{
  "action": "xero.listContacts",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "where": "ContactStatus==\"ACTIVE\"",
    "order": "Name ASC",
    "page": 1,
    "includeArchived": false
  }
}

Invoice Actions

getInvoice

Get invoice by ID.

{
  "action": "xero.getInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}"
  }
}

createInvoice

Create a sales invoice (ACCREC).

{
  "id": "create-invoice",
  "type": "action",
  "action": "xero.createInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "type": "ACCREC",
    "contactId": "{{input.contactId}}",
    "date": "{{input.invoiceDate}}",
    "dueDate": "{{input.dueDate}}",
    "lineAmountTypes": "Exclusive",
    "invoiceNumber": "INV-{{input.invoiceNumber}}",
    "reference": "{{input.poNumber}}",
    "currencyCode": "USD",
    "status": "DRAFT",
    "lineItems": [
      {
        "Description": "{{input.items[0].description}}",
        "Quantity": "{{input.items[0].quantity}}",
        "UnitAmount": "{{input.items[0].unitPrice}}",
        "AccountCode": "200",
        "TaxType": "OUTPUT"
      }
    ]
  }
}

Invoice Types:

  • ACCREC - Accounts Receivable (Sales Invoice)
  • ACCPAY - Accounts Payable (Bill)

Line Amount Types:

  • Exclusive - Amounts exclude tax
  • Inclusive - Amounts include tax
  • NoTax - No tax applied

updateInvoice

Update an invoice.

{
  "action": "xero.updateInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}",
    "dueDate": "{{input.newDueDate}}",
    "status": "SUBMITTED"
  }
}

voidInvoice

Void an invoice.

{
  "action": "xero.voidInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}"
  }
}

deleteInvoice

Delete a draft invoice.

{
  "action": "xero.deleteInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}"
  }
}

listInvoices

List invoices with filters.

{
  "action": "xero.listInvoices",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "where": "Type==\"ACCREC\" AND Status==\"AUTHORISED\"",
    "order": "Date DESC",
    "page": 1,
    "statuses": ["DRAFT", "SUBMITTED", "AUTHORISED"]
  }
}

emailInvoice

Send invoice via email.

{
  "action": "xero.emailInvoice",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}"
  }
}

Bill Actions

createBill

Create a bill (accounts payable).

{
  "action": "xero.createBill",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "contactId": "{{input.vendorId}}",
    "date": "{{input.billDate}}",
    "dueDate": "{{input.dueDate}}",
    "lineItems": [
      {
        "Description": "{{input.description}}",
        "Quantity": 1,
        "UnitAmount": "{{input.amount}}",
        "AccountCode": "400"
      }
    ],
    "status": "AUTHORISED"
  }
}

Payment Actions

getPayment

Get payment by ID.

{
  "action": "xero.getPayment",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "paymentId": "{{input.paymentId}}"
  }
}

createPayment

Record a payment against an invoice.

{
  "id": "record-payment",
  "type": "action",
  "action": "xero.createPayment",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "invoiceId": "{{input.invoiceId}}",
    "accountId": "{{input.bankAccountId}}",
    "amount": "{{input.paymentAmount}}",
    "date": "{{input.paymentDate}}",
    "reference": "{{input.transactionRef}}"
  }
}

deletePayment

Delete an unreconciled payment.

{
  "action": "xero.deletePayment",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "paymentId": "{{input.paymentId}}"
  }
}

listPayments

List payments.

{
  "action": "xero.listPayments",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "where": "Date>=DateTime(2024,01,01)",
    "order": "Date DESC",
    "page": 1
  }
}

Bank Transaction Actions

getBankTransaction

Get bank transaction by ID.

{
  "action": "xero.getBankTransaction",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "bankTransactionId": "{{input.transactionId}}"
  }
}

createBankTransaction

Create a bank transaction (spend or receive).

{
  "id": "record-expense",
  "type": "action",
  "action": "xero.createBankTransaction",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "type": "SPEND",
    "contactId": "{{input.vendorId}}",
    "bankAccountId": "{{input.bankAccountId}}",
    "date": "{{input.date}}",
    "reference": "{{input.reference}}",
    "lineAmountTypes": "Exclusive",
    "lineItems": [
      {
        "Description": "{{input.description}}",
        "Quantity": 1,
        "UnitAmount": "{{input.amount}}",
        "AccountCode": "429"
      }
    ],
    "status": "AUTHORISED"
  }
}

Transaction Types:

  • SPEND - Money out (expense)
  • RECEIVE - Money in (income)

updateBankTransaction

Update a bank transaction.

{
  "action": "xero.updateBankTransaction",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "bankTransactionId": "{{input.transactionId}}",
    "reference": "{{input.newReference}}"
  }
}

listBankTransactions

List bank transactions.

{
  "action": "xero.listBankTransactions",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "bankAccountId": "{{input.bankAccountId}}",
    "where": "Type==\"SPEND\"",
    "page": 1
  }
}

Account Actions

getAccount

Get account by ID.

{
  "action": "xero.getAccount",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "accountId": "{{input.accountId}}"
  }
}

createAccount

Create a new account.

{
  "action": "xero.createAccount",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "name": "Software Subscriptions",
    "code": "425",
    "type": "EXPENSE",
    "description": "Monthly SaaS subscriptions",
    "taxType": "INPUT"
  }
}

Account Types: BANK, CURRENT, CURRLIAB, DEPRECIATN, DIRECTCOSTS, EQUITY, EXPENSE, FIXED, INVENTORY, LIABILITY, NONCURRENT, OTHERINCOME, OVERHEADS, PREPAYMENT, REVENUE, SALES, TERMLIAB

listAccounts

List chart of accounts.

{
  "action": "xero.listAccounts",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "where": "Type==\"EXPENSE\"",
    "order": "Code"
  }
}

Credit Note Actions

getCreditNote

Get credit note by ID.

{
  "action": "xero.getCreditNote",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "creditNoteId": "{{input.creditNoteId}}"
  }
}

createCreditNote

Create a credit note.

{
  "action": "xero.createCreditNote",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "type": "ACCRECCREDIT",
    "contactId": "{{input.contactId}}",
    "date": "{{input.date}}",
    "lineAmountTypes": "Exclusive",
    "lineItems": [
      {
        "Description": "Refund for {{input.reason}}",
        "Quantity": 1,
        "UnitAmount": "{{input.amount}}",
        "AccountCode": "200"
      }
    ],
    "status": "AUTHORISED"
  }
}

Credit Note Types:

  • ACCRECCREDIT - Customer credit (AR)
  • ACCPAYCREDIT - Supplier credit (AP)

Item Actions

getItem

Get item by ID.

{
  "action": "xero.getItem",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "itemId": "{{input.itemId}}"
  }
}

createItem

Create a product/service item.

{
  "action": "xero.createItem",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "code": "PROD-001",
    "name": "{{input.productName}}",
    "description": "{{input.description}}",
    "isSold": true,
    "isPurchased": false,
    "salesDetails": {
      "UnitPrice": "{{input.price}}",
      "AccountCode": "200"
    }
  }
}

listItems

List items.

{
  "action": "xero.listItems",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "where": "IsSold==true"
  }
}

Organisation Actions

getOrganisation

Get organisation details.

{
  "action": "xero.getOrganisation",
  "config": {
    "tenantId": "{{input.tenantId}}"
  }
}

Report Actions

getReport

Get a financial report.

{
  "action": "xero.getReport",
  "config": {
    "tenantId": "{{input.tenantId}}",
    "reportType": "ProfitAndLoss",
    "fromDate": "2024-01-01",
    "toDate": "2024-12-31",
    "periods": 12,
    "timeframe": "MONTH"
  }
}

Report Types:

  • BalanceSheet
  • ProfitAndLoss
  • TrialBalance
  • AgedPayablesByContact
  • AgedReceivablesByContact
  • BudgetSummary

Webhook Triggers

Xero webhooks trigger workflows on accounting events.

{
  "trigger": {
    "type": "webhook",
    "provider": "xero",
    "events": ["INVOICE.CREATED", "PAYMENT.CREATED"]
  }
}

Event Types:

EventDescription
INVOICE.CREATEDInvoice created
INVOICE.UPDATEDInvoice updated
CONTACT.CREATEDContact created
CONTACT.UPDATEDContact updated
PAYMENT.CREATEDPayment recorded

Example Workflow

Invoice sync workflow:

{
  "name": "Sync Stripe Invoice to Xero",
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["invoice.finalized"]
  },
  "steps": [
    {
      "id": "find-contact",
      "type": "action",
      "action": "xero.listContacts",
      "config": {
        "tenantId": "{{env.XERO_TENANT_ID}}",
        "where": "EmailAddress==\"{{input.data.object.customer_email}}\""
      }
    },
    {
      "id": "check-contact",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{steps.find-contact.output.Contacts.length}}",
          "operator": "gt",
          "right": 0
        },
        "then": ["create-invoice"],
        "else": ["create-contact"]
      }
    },
    {
      "id": "create-contact",
      "type": "action",
      "action": "xero.createContact",
      "config": {
        "tenantId": "{{env.XERO_TENANT_ID}}",
        "name": "{{input.data.object.customer_name}}",
        "emailAddress": "{{input.data.object.customer_email}}",
        "isCustomer": true
      }
    },
    {
      "id": "create-invoice",
      "type": "action",
      "action": "xero.createInvoice",
      "config": {
        "tenantId": "{{env.XERO_TENANT_ID}}",
        "type": "ACCREC",
        "contactId": "{{steps.find-contact.output.Contacts[0].ContactID || steps.create-contact.output.Contacts[0].ContactID}}",
        "date": "{{input.data.object.created | date: 'YYYY-MM-DD'}}",
        "dueDate": "{{input.data.object.due_date | date: 'YYYY-MM-DD'}}",
        "reference": "Stripe {{input.data.object.number}}",
        "lineItems": [
          {
            "Description": "{{input.data.object.lines.data[0].description}}",
            "Quantity": 1,
            "UnitAmount": "{{input.data.object.amount_due | divided_by: 100}}",
            "AccountCode": "200"
          }
        ],
        "status": "AUTHORISED"
      }
    }
  ]
}

Rate Limits

LimitValue
API calls/minute60
API calls/day5,000
Concurrent connections5 per app

Troubleshooting

Common Errors

ErrorCauseSolution
tenantId is requiredMissing organization IDCall getConnections first
contactId is requiredNo contact specifiedProvide contactId
INVALID_INVOICE_STATUSWrong status transitionCheck current status
VALIDATION_ERRORMissing required fieldsReview required parameters
ORGANIZATION_OFFLINEOrg temporarily unavailableRetry later

Where Filter Syntax

Xero uses OData-style filters:

// String comparison
where: "ContactStatus==\"ACTIVE\""

// Date comparison
where: "Date>=DateTime(2024,01,01)"

// Numeric comparison
where: "AmountDue>0"

// Multiple conditions (AND)
where: "Type==\"ACCREC\" AND Status==\"AUTHORISED\""