Xero
Sync contacts and invoices from your data warehouse to Xero. Keep your Xero accounting records enriched with the latest customer data, contact details, and invoice information from your central data platform.
Prerequisites
- A Xero account (any plan)
- A Xero OAuth 2.0 app registered in the Xero Developer Portal
- The Xero organisation tenant ID (UUID) for the target organisation
- OAuth scopes:
accounting.contactsand/oraccounting.transactionsdepending on your sync target
Permissions
Your Xero OAuth app must request the following scopes:
| Scope | Required For |
|---|---|
openid | OAuth 2.0 authentication |
profile | OAuth 2.0 authentication |
email | OAuth 2.0 authentication |
offline_access | Token refresh (recommended) |
accounting.contacts | Syncing contacts |
accounting.transactions | Syncing invoices |
The Xero user who authorises the app must have Adviser or Standard role access to the target organisation. Users with Invoice Only or Read Only access cannot create or update records.
Authentication
Xero uses OAuth 2.0 for all API access. API key authentication is not supported.
OAuth 2.0 (Recommended)
- Log in to the Xero Developer Portal
- Create a new app or select your existing app
- Note your Client ID and Client Secret
- In Zeotap, select OAuth 2.0 as the auth method
- Click Connect to begin the OAuth flow
- Authorise Zeotap to access your Xero organisation
- Zeotap will automatically refresh your access token every 30 minutes
Access Token
If you already have a valid Xero access token (for testing or custom integrations):
- Select Access Token as the auth method
- Paste your access token into the Access Token field
- Note: Xero access tokens expire after 30 minutes. For production use, OAuth 2.0 is strongly recommended.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
| Tenant ID | Text | Yes | The Xero organisation tenant ID (UUID). This identifies which Xero organisation to sync data to. You can find it via the OAuth connections endpoint or in the Xero-Tenant-Id header after connecting. |
Finding Your Tenant ID
After completing OAuth authorisation, call the Xero Connections endpoint to list your authorised tenants:
curl -H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
https://api.xero.com/connectionsThe response includes the tenantId (UUID) for each authorised organisation.
Target Settings
| Field | Type | Required | Description |
|---|---|---|---|
| Xero Object | Select | Yes | The Xero object to sync data to: Contacts or Invoices. |
| ID Field | Text | No | The name of the mapped field that contains the Xero ContactID or InvoiceID (UUID) for update and upsert operations. Leave blank to always create new records in insert mode. |
Supported Operations
Sync Modes
| Mode | Supported |
|---|---|
| Upsert | Yes |
| Insert | Yes |
| Update | Yes |
| Mirror | — |
Audience Sync Modes
| Mode | Supported |
|---|---|
| Add | — |
| Remove | — |
| Mirror | — |
| Upsert | — |
Xero does not have a native audience or list membership concept. Contact Groups exist in Xero but are managed separately from the standard contact sync. Audience sync modes are not supported.
Features
- Field Mapping: Yes
- Schema Introspection: No — Xero contact and invoice fields are well-known and provided as default destination fields
Required Mapping Fields
| Object | Required Fields | Description |
|---|---|---|
| Contacts | Name | Contact name (person or organisation name) |
| Invoices | Contact.ContactID | The Xero ContactID (UUID) for the invoice recipient |
| Invoices | LineItems | Invoice line items as a JSON array. Each item needs at minimum a Description field. |
Default Destination Fields
Contact Fields
| Field | Type | Description |
|---|---|---|
Name | string | Contact name (person or organisation). Required. |
FirstName | string | Contact’s first name |
LastName | string | Contact’s last name |
EmailAddress | string | Contact’s email address |
AccountNumber | string | Contact’s account number in your system |
BankAccountDetails | string | Contact’s bank account details |
TaxNumber | string | Tax identification number |
CompanyNumber | string | Company registration number |
IsCustomer | boolean | Whether the contact is a customer |
IsSupplier | boolean | Whether the contact is a supplier |
DefaultCurrency | string | Default currency code (e.g. USD, GBP, NZD) |
Invoice Fields
| Field | Type | Description |
|---|---|---|
Type | string | ACCREC (sales invoice) or ACCPAY (bill). Defaults to ACCREC. |
Contact.ContactID | string | The Xero ContactID (UUID) for the invoice contact. Required. |
LineItems | json | JSON array of line items. Each needs at minimum a Description. |
Date | string | Invoice date in YYYY-MM-DD format |
DueDate | string | Payment due date in YYYY-MM-DD format |
InvoiceNumber | string | Unique invoice number or reference |
Reference | string | Additional reference for the invoice |
CurrencyCode | string | Currency code (e.g. USD, GBP, NZD) |
Status | string | Invoice status: DRAFT, SUBMITTED, or AUTHORISED |
How It Works
-
Field Mapping: Zeotap maps your warehouse columns to Xero contact or invoice fields. Field names must match Xero’s API field names (PascalCase, e.g.
FirstName,EmailAddress). -
Batch Requests: Xero accepts up to 50 records per API request. Zeotap automatically chunks your data into batches of 50 and sends them as JSON payloads.
-
Request Format: All requests use JSON (
application/json) with theAuthorization: Bearer {token}andXero-Tenant-Id: {tenant_id}headers. Items are wrapped in a named array:{ "Contacts": [ { "Name": "Acme Corp", "EmailAddress": "billing@acme.com" } ] } -
Sync Mode Behavior (all modes use the Xero
POST /ContactsorPOST /Invoicesendpoint, which is documented as “create or update”):- Insert: Sends
POSTwithout a ContactID or InvoiceID. Xero creates new records. - Update: Sends
POSTwith the ContactID or InvoiceID populated from the configured ID field. Xero updates the existing record. - Upsert: Sends
POSTwith the ContactID or InvoiceID included when available. Xero matches on the ID and updates existing records, or creates new ones if no ID is provided.
- Insert: Sends
-
Invoice Line Items: The
LineItemsfield should contain a JSON array. Each line item requires at minimum aDescription. Example:[ { "Description": "Consulting services", "Quantity": 10, "UnitAmount": 150.00, "AccountCode": "200" } ] -
Error Handling: Xero returns per-item validation errors in the response body. Zeotap parses these and reports row-level errors with the specific validation message from Xero. Transient errors (429 rate limits, 5xx server errors) are retried automatically with exponential backoff.
Rate Limits
Xero enforces multiple rate limit tiers:
| Limit | Threshold |
|---|---|
| Per-minute per tenant | 60 API calls |
| Daily per tenant | 5,000 API calls |
| App-wide per minute | 10,000 API calls |
| Concurrent requests | 5 simultaneous |
Each API response includes rate limit headers:
X-MinLimit-Remaining— remaining calls in the current minuteX-DayLimit-Remaining— remaining calls for the dayX-AppMinLimit-Remaining— remaining app-wide calls
Zeotap automatically handles rate limiting:
- HTTP 429 responses trigger exponential backoff with retry
- The
Retry-Afterheader is respected when present - Up to 3 retries per request
With batching at 50 records per request, syncing 1,000 contacts requires only 20 API calls, well within the per-minute limit.
Best Practices
- Use OAuth 2.0: Xero access tokens expire after 30 minutes. OAuth 2.0 with
offline_accessscope lets Zeotap refresh tokens automatically. Static access tokens will stop working after 30 minutes. - Map the ID field: For upsert and update modes, configure the ID Field in target settings and map a column containing the Xero ContactID or InvoiceID (UUID). Without it, upsert mode creates duplicate records.
- Use PascalCase field names: Xero API fields use PascalCase (
FirstName,EmailAddress,AccountNumber). Ensure your field mappings match this casing exactly. - Validate line items: For invoice syncs, ensure the
LineItemscolumn contains valid JSON arrays. Malformed JSON will cause the entire row to fail. - Monitor daily limits: With only 5,000 API calls per day per tenant, plan your sync schedules to avoid hitting the daily cap, especially if other integrations share the same Xero app.
- Test with a demo company: Xero offers free demo companies for testing. Configure your destination with a demo company tenant ID before syncing to production.
Troubleshooting
Authentication failed: invalid or expired access token
Xero access tokens expire after 30 minutes. If using OAuth 2.0, Zeotap refreshes tokens automatically. If using a static access token, generate a new one. Ensure the offline_access scope is requested during OAuth to enable token refresh.
Tenant ID not found or forbidden
The tenant ID must be a valid UUID for a Xero organisation that the authorised user has access to. Verify the tenant ID by calling the Connections endpoint (GET https://api.xero.com/connections). If you have access to multiple organisations, ensure you are using the correct tenant ID.
Contact name is required
Xero requires a Name field for all contacts. Ensure your field mapping includes a Name column with a non-empty value for every row. The name can be a person’s full name or an organisation name.
Invoice line items validation error
The LineItems field must be a valid JSON array. Each line item requires at minimum a Description field. Common issues:
- The JSON is malformed or not an array
- Line items are missing the
Descriptionfield AccountCodereferences a code that does not exist in your Xero chart of accounts
Rate limiting (429 errors)
Xero limits API calls to 60 per minute per tenant. Zeotap retries automatically, but persistent rate limits may indicate:
- Too many concurrent syncs targeting the same Xero organisation
- Other applications sharing the same Xero app or tenant
- Very large syncs that exceed the daily limit of 5,000 calls
Reduce sync frequency or batch size if rate limits persist.
Contact or invoice not found in update mode
Update mode requires a valid Xero ContactID or InvoiceID (UUID) for each row. Verify that:
- The ID Field is configured in target settings
- The mapped column contains valid Xero UUIDs (format:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) - The records exist in the target Xero organisation
Duplicate contacts created in upsert mode
If upsert creates duplicates instead of updating, the ID field is likely not configured or the mapped column contains empty values. Xero identifies existing records by their ContactID, not by name or email. Configure the ID Field and ensure every row includes the Xero ContactID for records that should be updated.
Currency code rejected
Xero only accepts ISO 4217 currency codes that have been enabled in your Xero organisation settings. Navigate to Settings > General Settings > Currencies in Xero and add the required currencies before syncing.