Connect Retool to Xero using a REST API Resource with OAuth 2.0. Configure Xero's API credentials in Retool's Resources tab, handle the tenant ID requirement for multi-organisation access, and build financial dashboards showing invoices, bills, bank transactions, and contacts. Xero's REST API covers international accounting workflows for UK, Australian, and New Zealand businesses.
| Fact | Value |
|---|---|
| Tool | Xero |
| Category | Other |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 30 minutes |
| Last updated | April 2026 |
Build International Accounting Dashboards with Xero and Retool
Xero is the dominant accounting platform in the UK, Australia, and New Zealand, with strong adoption across other international markets. Finance teams using Xero often need operational dashboards that surface invoice status, accounts receivable aging, bank transaction summaries, and contact payment histories — information that Xero's built-in reports provide but that can't be easily customised or combined with data from other systems. Retool bridges this gap by connecting to Xero's Accounting API and presenting financial data in fully customisable dashboards.
Xero's REST API covers the complete accounting data model: invoices (both accounts receivable and accounts payable), contacts (customers and suppliers), bank transactions, payments, credit notes, purchase orders, expense claims, payroll, and financial statements. Every API call must include a Xero-tenant-id header identifying which Xero organisation to query — this is Xero's mechanism for multi-organisation support, where a single API credential can access multiple client organisations. In Retool, you configure this header once in the Resource settings or pass it dynamically from a query parameter.
Building a Retool dashboard on top of Xero gives finance teams capabilities that the Xero interface doesn't natively provide: cross-filtering invoices by custom criteria, joining Xero contacts with your CRM data, building approval workflows for bills before they're paid, and automating repetitive data entry tasks through Retool forms that write back to Xero's API. The integration is particularly powerful for accounting firms managing multiple Xero client organisations from a single Retool instance.
Integration method
Xero integrates with Retool through Xero's REST API, configured as a REST API Resource. Authentication uses OAuth 2.0 with the Authorization Code flow. A key Xero-specific requirement is the Xero-tenant-id header, which must be included in every API request to identify which Xero organisation you are querying. Retool proxies all requests server-side, keeping OAuth tokens and tenant IDs off the client browser.
Prerequisites
- A Xero account with at least one connected organisation
- A Xero Developer application created at developer.xero.com with OAuth 2.0 credentials (Client ID and Client Secret)
- Your Xero tenant ID (obtainable by calling the Xero connections endpoint after authenticating)
- A Retool account with permission to create and configure Resources
- Understanding of Xero's Accounting API structure (invoices, contacts, bank transactions)
Step-by-step guide
Create a Xero OAuth 2.0 application
Go to developer.xero.com and sign in with your Xero credentials. Navigate to 'My Apps' and click 'New app'. Choose the app type — for a Retool integration that accesses your own Xero organisation, select 'Web app' to use the Authorization Code OAuth flow. Fill in the application name (e.g., 'Retool Finance Dashboard'), your company's URL, and the redirect URI. For Retool Cloud, the redirect URI is https://oauth.retool.com/oauth/user/oauthcallback. For self-hosted Retool, use your BASE_DOMAIN value followed by /oauth/user/oauthcallback. You can add multiple redirect URIs if needed. Under OAuth 2.0 scopes, select the permissions your dashboard needs. For a read-only financial dashboard: openid (required), profile (required), email (required), accounting.transactions.read (invoices, bills, bank transactions), accounting.contacts.read (customers, suppliers), and accounting.settings.read (organisation details). If your Retool app needs to create or update records, add the write variants of these scopes. After saving the app, Xero shows your Client ID. Click 'Generate a secret' to create your Client Secret. Copy both values — they will not be shown again. Store them securely; you'll paste them into the Retool Resource configuration in the next step.
Pro tip: The Xero Developer application scopes are separate from Xero organisation permissions — even with all API scopes granted, users can only access organisations they're actually connected to in Xero.
Expected result: You have a Xero OAuth 2.0 application with a Client ID and Client Secret, the correct scopes, and the Retool redirect URI configured.
Configure the Xero REST API Resource in Retool
In Retool, open the Resources tab from the left sidebar navigation. Click 'Create a resource' and select 'REST API'. Name the resource 'Xero API'. Set the base URL to https://api.xero.com/api.xro/2.0. This is the base URL for Xero's Accounting API v2 — all queries targeting accounting data (invoices, contacts, transactions) will use paths relative to this URL. For authentication, select 'OAuth 2.0'. Enter the Client ID and Client Secret from your Xero developer application. Set the Authorization URL to https://login.xero.com/identity/connect/authorize and the Token URL to https://identity.xero.com/connect/token. In the Scope field, enter the scopes you configured: openid profile email accounting.transactions.read accounting.contacts.read accounting.settings.read (space-separated). Next, add a custom header that all Xero queries require. Under 'Headers', click 'Add header'. Set the name to Xero-tenant-id. For the value, you have two options: enter your specific tenant ID directly if you're only connecting to one organisation, or use a configuration variable with {{ retoolContext.configVars.XERO_TENANT_ID }} to make it configurable without changing the resource settings. Also add an Accept header with value application/json. Save the resource and click 'Connect' to complete the OAuth flow. Retool will open a Xero authentication popup — sign in and grant access to your organisation. After authorizing, the resource status should show as connected.
Pro tip: If you don't know your Xero tenant ID yet, temporarily set the Xero-tenant-id header to a placeholder, save the resource, and then run a GET query to https://api.xero.com/connections to list all connected organisations and their tenant IDs. Then update the resource header with the correct value.
Expected result: The Xero API resource is configured in Retool with OAuth 2.0 authentication and the required Xero-tenant-id header. The resource shows as connected.
Retrieve your Xero tenant ID
The Xero-tenant-id is essential — every API call to Xero's Accounting API requires it to identify which organisation you're querying. To find your tenant ID, create a temporary resource or query pointing to Xero's connections endpoint. Create a new query in a Retool app. Select your Xero API resource. Set the method to GET and the path to /Connections. Note: this endpoint uses the Xero base URL https://api.xero.com (not the Accounting API URL), so you may need to override the base URL in the query or create a separate 'Xero Identity' resource with base URL https://api.xero.com. Run the query. The response is an array of connection objects, each containing tenantId (the value you need), tenantType (ORGANISATION or PRACTICE), tenantName (your Xero organisation name), and createdDateUtc. Copy the tenantId value for the organisation you want to connect to. For accounting firms managing multiple clients, store all tenant IDs in a configuration variable or Retool Database table with associated client names. Build a Select dropdown in your Retool apps that lets users choose a client organisation — then pass the corresponding tenant ID in the Xero-tenant-id header of each query using {{ organisationSelect.value }}.
1// Response from GET https://api.xero.com/connections2// Example response structure:3[4 {5 "id": "abc123de-f456-7890-abcd-ef1234567890",6 "authEventId": "abc123de-f456-7890-abcd-ef1234567890",7 "tenantId": "12345678-abcd-1234-abcd-123456789012",8 "tenantType": "ORGANISATION",9 "tenantName": "Acme Ltd",10 "createdDateUtc": "/Date(1609459200000)/",11 "updatedDateUtc": "/Date(1609459200000)/"12 }13]Pro tip: Copy the tenantId (not the id field) from the connections response — these are two different UUIDs and only tenantId works for the Xero-tenant-id header.
Expected result: You have retrieved your Xero tenant ID and updated the Xero-tenant-id header in the Resource settings. Queries can now successfully target your Xero organisation.
Build invoice and financial overview queries
With the resource configured and tenant ID in place, create the core data queries for your financial dashboard. In a new Retool app, create a query named 'getInvoices'. Select the Xero API resource, set method to GET, and path to /Invoices. Add URL parameters: Type (set to ACCREC for accounts receivable invoices, or leave blank for all), Statuses (set to AUTHORISED,SUBMITTED for unpaid invoices), page (set to {{ pagination.currentPage || 1 }} for pagination), and pageSize (set to 100). Xero's Invoices endpoint returns detailed invoice objects with fields for contact name, amounts, due dates, and line items. Create a second query 'getContacts' targeting /Contacts with parameters like where (Xero's filter syntax, e.g., IsCustomer=true) and summaryOnly=true for faster loading. Create a third query 'getBankSummary' targeting /BankAccounts to pull account balances. For bank transactions, use /BankTransactions with date range parameters. Note that Xero uses its own date format: /Date(1609459200000+0000)/ for JSON responses. Your transformer must parse these. Create a transformer to convert Xero dates and reshape the response for the dashboard.
1// JavaScript transformer for Xero invoice data2const invoices = data.Invoices || [];34// Helper: parse Xero's /Date(timestamp+offset)/ format5function parseXeroDate(xeroDate) {6 if (!xeroDate) return null;7 const match = xeroDate.match(/\/Date\((\d+)[+-]\d+\)\//);8 if (!match) return xeroDate;9 return new Date(parseInt(match[1])).toLocaleDateString('en-GB', {10 year: 'numeric', month: 'short', day: 'numeric'11 });12}1314return invoices.map(inv => ({15 invoiceNumber: inv.InvoiceNumber || 'N/A',16 contact: inv.Contact?.Name || 'Unknown',17 status: inv.Status,18 issueDate: parseXeroDate(inv.Date),19 dueDate: parseXeroDate(inv.DueDate),20 total: `${inv.CurrencyCode} ${inv.Total?.toFixed(2) || '0.00'}`,21 amountDue: `${inv.CurrencyCode} ${inv.AmountDue?.toFixed(2) || '0.00'}`,22 daysOverdue: inv.DueDate23 ? Math.max(0, Math.floor((Date.now() - parseInt(inv.DueDate.match(/\d+/)[0])) / 86400000))24 : 0,25 invoiceId: inv.InvoiceID26}));Pro tip: Xero's API uses PascalCase for all field names (Contact, InvoiceNumber, AmountDue), unlike most REST APIs which use snake_case or camelCase — be careful with field references in transformers.
Expected result: Invoice, contact, and bank queries return data and the transformer correctly parses Xero's date format. The dashboard is ready for components to be bound to this data.
Build the financial dashboard UI
Build the Retool financial dashboard components. At the top of the canvas, add a Stats Row using three Statistic components showing total accounts receivable (sum of AmountDue for unpaid ACCREC invoices), total overdue amount (invoices where daysOverdue > 0), and number of outstanding invoices. Bind the Statistic component values using JavaScript expressions: {{ getInvoices_transformed.value.reduce((sum, inv) => sum + parseFloat(inv.amountDue.replace(/[^0-9.]/g, '') || 0), 0).toFixed(2) }}. Below the stats, add a Bar Chart component. Set the dataset to a query or transformer that groups invoices by aging bucket. Use the 'Group by' feature in a separate transformer that categorises invoices as 'Current (0-30)', 'Overdue 31-60', 'Overdue 61-90', and 'Overdue 90+'. Add a Table component showing the transformed invoice data. Configure columns: invoiceNumber, contact, status (as a Tag), issueDate, dueDate, amountDue, and a 'View in Xero' action button that constructs a Xero web URL and opens it in a new tab. Add filter components above the table: a Select for invoice status (AUTHORISED, SUBMITTED, PAID), a TextInput for contact name search, and a Date Range picker for due date filtering. Connect these to the query parameters in your getInvoices query. For complex multi-resource dashboards combining Xero with your CRM or payment system, RapidDev's team can help design the data joining logic and query architecture.
1// Transformer to group invoices into aging buckets for the bar chart2const invoices = getInvoices_transformed.value || [];34const buckets = {5 'Current (0-30 days)': 0,6 'Overdue 31-60 days': 0,7 'Overdue 61-90 days': 0,8 'Overdue 90+ days': 09};1011invoices.forEach(inv => {12 const days = inv.daysOverdue || 0;13 const amount = parseFloat(inv.amountDue.replace(/[^0-9.]/g, '') || 0);14 if (days <= 30) buckets['Current (0-30 days)'] += amount;15 else if (days <= 60) buckets['Overdue 31-60 days'] += amount;16 else if (days <= 90) buckets['Overdue 61-90 days'] += amount;17 else buckets['Overdue 90+ days'] += amount;18});1920return Object.entries(buckets).map(([label, value]) => ({21 bucket: label,22 amount: parseFloat(value.toFixed(2))23}));Pro tip: Xero invoice URLs follow the pattern https://go.xero.com/AccountsReceivable/View.aspx?InvoiceID={invoiceId} — use this in your 'View in Xero' button action to deep-link directly to the invoice.
Expected result: The Retool financial dashboard displays outstanding invoice totals, an aging chart, and a filterable invoice table. Users can filter by status, contact, and date range and click through to view invoices directly in Xero.
Common use cases
Accounts receivable aging dashboard
Build a dashboard showing all outstanding invoices grouped by aging buckets (0-30 days, 31-60 days, 61-90 days, 90+ days). Finance teams can filter by contact, see total outstanding amounts per bucket, and click through to individual invoice details — all in one view that Xero's native reports don't easily produce.
Build a Retool app with a Bar Chart showing invoice counts by aging bucket and a Table below showing individual outstanding invoices with columns for contact name, invoice number, due date, amount due, and days overdue. Add a Select input to filter by contact and a Date Range picker to filter by due date.
Copy this prompt to try it in Retool
Supplier bill payment operations panel
Build a bill approval and payment workflow where finance managers can see all bills awaiting payment, filter by supplier and amount, mark bills as approved, and trigger payments directly from Retool. The panel connects Xero's bills data with your internal approval process before executing payments.
Build a Retool dashboard showing Xero bills with AUTHORISED status. Include columns for supplier name, bill date, due date, amount, and an Approve button. When approved, mark the bill status and update a separate tracking database. Add a total outstanding amount summary at the top.
Copy this prompt to try it in Retool
Multi-client accounting overview for accounting firms
For accounting firms managing multiple Xero organisations, build a master dashboard that loops through multiple tenant IDs and shows a summary of each client's outstanding invoices, bank balances, and overdue bills on a single screen — eliminating the need to switch between Xero accounts.
Build a Retool app with a Select dropdown listing client organisations (each mapped to a Xero tenant ID). When a client is selected, load their Xero dashboard showing total accounts receivable, total accounts payable, current bank balance, and a list of the 10 most recent transactions.
Copy this prompt to try it in Retool
Troubleshooting
Xero API queries return 403 Forbidden with message 'AuthenticationUnsuccessful' or 'TenantIdNotFound'
Cause: The Xero-tenant-id header is missing, incorrect, or not matching the authenticated user's connected organisations. This is the most common Xero API error.
Solution: Verify your tenant ID by running a GET query to https://api.xero.com/connections (you may need a separate resource with base URL https://api.xero.com and no Accounting API path). Copy the tenantId field (not the id field) from the response and update your Resource's Xero-tenant-id header. Ensure the header name is exactly 'Xero-tenant-id' (case-sensitive, with hyphen).
OAuth token expires and Retool queries fail with 401 Unauthorized after 30 minutes
Cause: Xero access tokens expire after 30 minutes. Retool should automatically refresh tokens using the refresh token, but this fails if the refresh token has expired (Xero refresh tokens expire after 60 days of inactivity).
Solution: In the Resource settings, ensure 'Automatically refresh tokens' is enabled. If the refresh token has expired, the resource will need to be re-authenticated: open Resource settings, scroll to the OAuth section, and click 'Reconnect'. Users may need to re-authorize through the Xero OAuth flow. To avoid this for production dashboards, ensure the Retool app is used at least once every 60 days to keep the refresh token active.
Date fields in Xero responses display as '/Date(1609459200000+0000)/' instead of readable dates
Cause: Xero uses a legacy .NET date format (Microsoft JSON Date format) for date fields in JSON responses instead of ISO 8601 format.
Solution: Use a JavaScript transformer to parse Xero dates. The pattern /Date(timestamp+offset)/ contains a Unix timestamp in milliseconds. Extract the number and pass it to JavaScript's Date constructor.
1// Parse Xero date format in a transformer2function parseXeroDate(xeroDate) {3 if (!xeroDate) return '';4 const match = xeroDate.match(/\/Date\((\d+)[+-]\d+\)\//);5 if (!match) return xeroDate;6 return new Date(parseInt(match[1])).toLocaleDateString();7}8// Usage: parseXeroDate(invoice.DueDate)Xero API returns fewer records than expected — page 1 shows 100 results but there are clearly more invoices
Cause: Xero's Invoices endpoint is paginated with a default page size of 100. Without explicit pagination parameters, only the first page is returned.
Solution: Add page and pageSize parameters to your query (page defaults to 1, pageSize maximum is 1000 for the Invoices endpoint). Implement a multi-page fetch using a Retool Workflow if you need all records: loop through pages until the response returns fewer results than pageSize, accumulating all results. For dashboards, use Retool's built-in Table pagination and pass the current page to the Xero page parameter.
Best practices
- Store your Xero tenant ID in a Retool configuration variable (Settings → Configuration Variables) rather than hardcoding it in Resource headers — this makes switching between Xero organisations straightforward without editing the resource
- Use read-only OAuth scopes (accounting.transactions.read, accounting.contacts.read) for dashboard-only apps — only add write scopes if your Retool app actually creates or modifies Xero data
- Implement query caching for financial summary data that doesn't change frequently — a 5-minute cache on the accounts receivable total reduces Xero API calls without sacrificing data freshness
- For multi-client accounting firm setups, store tenant IDs and client names in a Retool Database table and build a client selector dropdown that passes the selected tenant ID in query headers
- Parse Xero's /Date(timestamp)/ format in a reusable transformer function that you call across all date fields — define it once and call it consistently rather than duplicating the regex in every transformer
- Use Xero's 'where' parameter for server-side filtering (e.g., Status=="AUTHORISED" AND AmountDue>0) rather than loading all records and filtering in Retool — Xero's API supports compound filter expressions
- When building payment workflows that write back to Xero (creating invoices, recording payments), always add a confirmation modal before executing write queries to prevent accidental duplicate payments or data entry errors
Alternatives
Choose QuickBooks if your business primarily operates in the United States — QuickBooks is the dominant accounting platform in the US market and uses a different realm-based multi-company architecture.
Choose FreshBooks if you're building dashboards for freelancers or small service businesses that need simple invoicing and time tracking rather than full double-entry accounting.
Choose Zoho Books if your team already uses the Zoho suite of products — it integrates natively with Zoho CRM and other Zoho apps and has strong international tax compliance features.
Frequently asked questions
What is a Xero tenant ID and why is it required for every API call?
A Xero tenant ID is a UUID that identifies a specific Xero organisation (or practice). Xero's OAuth 2.0 model allows a single set of API credentials to be authorised across multiple organisations — the tenant ID tells Xero which organisation to query for each request. Without it, Xero doesn't know which of your connected organisations you're targeting. You retrieve it by calling the /connections endpoint immediately after authentication.
Can one Retool resource access multiple Xero organisations?
Yes. Configure the Resource with a dynamic Xero-tenant-id header using a component value: set the header value to {{ organisationSelect.value }} and populate the Select dropdown with all your tenant IDs. When users switch organisations, the header value changes and subsequent queries target the new organisation. Each authenticated OAuth token can access all organisations the user has been invited to in Xero.
Does Xero's API support real-time data or is there a delay?
Xero's API provides near-real-time data — changes made in Xero are typically available via the API within seconds. However, Xero enforces rate limits of 60 calls per minute and 5,000 calls per day for standard connections. For high-frequency dashboards, implement Retool query caching to avoid hitting these limits, and use Xero's LastModifiedBefore/LastModifiedAfter parameters to only fetch recently changed records.
Can I use this Retool integration to create invoices or record payments in Xero?
Yes, if your OAuth application includes write scopes (accounting.transactions). You can POST to /Invoices to create invoices, POST to /Payments to record payments, and PUT to update existing records. Always add confirmation modals and input validation in Retool before executing write operations, as Xero has strict validation rules (e.g., an invoice can only be paid if its status is AUTHORISED and the payment amount matches or is less than the outstanding balance).
What Xero plan is required to use the API?
Xero's API access is available across all paid Xero plans (Starter, Standard, Premium, Ultimate). The free trial also includes API access. There are no per-API-call charges — usage is billed as part of your Xero subscription. However, if you're building an integration to distribute to multiple Xero users, you may need to review Xero's API Partner programme requirements.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation