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 taxInclusive- Amounts include taxNoTax- 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:
BalanceSheetProfitAndLossTrialBalanceAgedPayablesByContactAgedReceivablesByContactBudgetSummary
Webhook Triggers
Xero webhooks trigger workflows on accounting events.
{
"trigger": {
"type": "webhook",
"provider": "xero",
"events": ["INVOICE.CREATED", "PAYMENT.CREATED"]
}
}Event Types:
| Event | Description |
|---|---|
INVOICE.CREATED | Invoice created |
INVOICE.UPDATED | Invoice updated |
CONTACT.CREATED | Contact created |
CONTACT.UPDATED | Contact updated |
PAYMENT.CREATED | Payment 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
| Limit | Value |
|---|---|
| API calls/minute | 60 |
| API calls/day | 5,000 |
| Concurrent connections | 5 per app |
Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
tenantId is required | Missing organization ID | Call getConnections first |
contactId is required | No contact specified | Provide contactId |
INVALID_INVOICE_STATUS | Wrong status transition | Check current status |
VALIDATION_ERROR | Missing required fields | Review required parameters |
ORGANIZATION_OFFLINE | Org temporarily unavailable | Retry 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\""