A simple, focused web application for querying Xero accounting data and exporting results to CSV. Built for Excel geeks who need direct API access without writing code every time.
- ✅ Pre-configured Query Scenarios: Unpaid invoices, recent payments, unreconciled transactions, and more
- ✅ Direct Xero API Access: Fast, efficient queries using Xero's API v2.0
- ✅ CSV Export: Every result can be exported for further analysis in Excel
- ✅ Aggregated Views: Automatic grouping and summing (e.g., by bank account)
- ✅ Session-Based Security: No database, no permanent credential storage
- ✅ Locale-Aware Formatting: Currency and numbers adapt to your regional settings
- ✅ Customizable: Add new queries by editing a JSON file—no code changes needed
- Framework: Next.js 14 (React 18)
- Language: TypeScript
- Styling: Tailwind CSS + shadcn/ui components
- API: Xero API v2.0 (OAuth2 client_credentials flow)
- Package Manager: Yarn
Before you begin, ensure you have:
- Node.js: Version 18 or higher
- Yarn: Version 1.22 or higher (or you can use npm)
- Xero Custom Connection:
- Go to developer.xero.com
- Create a new app and select "Custom connection"
- Add your organization
- Copy your Client ID and Client Secret
-
Clone the repository:
git clone https://github.com/cclambie/xero-query-tool.git cd xero-query-tool -
Install dependencies:
yarn install
-
Set up environment variables:
cp .env.example .env
Edit
.envand configure:NEXTAUTH_URL=http://localhost:3000 -
Run the development server:
yarn dev
-
Open your browser: Navigate to http://localhost:3000
-
Enter Credentials:
- Input your Xero Custom Connection Client ID
- Input your Xero Custom Connection Client Secret
- These are session-only and never stored permanently
-
Select a Scenario:
- Choose from pre-configured queries like "Unpaid Invoices" or "Recent Payments"
- Set any required parameters (date ranges, etc.)
-
Execute Query:
- Click "Execute Query"
- Results appear in a sortable table
-
Export to CSV:
- Click "Export to CSV" to download results
- Open in Excel for further analysis
All query scenarios are defined in public/scenarios.json. To add a new query:
- Open
public/scenarios.json - Add a new scenario object:
{
"id": "your-query-id",
"name": "Display Name",
"description": "What this query does",
"endpoint": "https://api.xero.com/api.xro/2.0/ResourceName",
"method": "GET",
"parameters": [
{
"name": "where",
"type": "hidden",
"value": "YourFilterExpression"
}
],
"displayFields": ["Field1", "Field2", "Field3"]
}- Save the file
- Refresh your browser—changes take effect immediately
For detailed configuration instructions, see the Query Configuration Guide.
Get all authorised invoices with outstanding amounts.
Shows: Invoice Number, Contact Name, Date, Due Date, Total, Amount Due, Status
Get invoices past their due date with amounts owed.
Shows: Invoice Number, Contact Name, Date, Due Date, Total, Amount Due, Status
Get payments received in the last 30/60/90 days.
Shows: Date, Payment Type, Amount, Reference, Status, Invoice Number
Get bank transactions filtered by custom date range.
Shows: Date, Type, Contact Name, Reference, Total, Status
Summary of unreconciled transactions grouped by bank account.
Shows: Bank Account, Count of Unreconciled, Balance on Xero
xero-query-tool/
├── app/
│ ├── api/
│ │ └── xero-query/ # API route for Xero queries
│ ├── layout.tsx # Root layout with theme provider
│ ├── page.tsx # Main application page
│ └── globals.css # Global styles
├── components/
│ ├── instructions.tsx # Expandable usage instructions
│ ├── results-table.tsx # Query results display + CSV export
│ ├── theme-provider.tsx # Light/dark mode support
│ └── ui/ # shadcn/ui components
├── lib/
│ ├── csv-export.ts # CSV export utility
│ ├── types.ts # TypeScript interfaces
│ ├── utils.ts # Utility functions
│ └── xero-auth.ts # Xero OAuth authentication
├── public/
│ ├── scenarios.json # Query scenario definitions
│ ├── favicon.svg # Site icon
│ └── og-image.png # Social sharing image
├── .env.example # Environment variable template
├── package.json # Dependencies and scripts
└── README.md # This file
This tool uses Xero's Custom Connection OAuth2 flow with the client_credentials grant type:
- User enters Client ID and Client Secret (session-only)
- Backend requests access token from Xero
- Token is cached with 5-minute expiry buffer
- All API requests use the access token
- First organization is automatically selected as tenant
Security Notes:
- Credentials are never stored in a database
- Tokens are cached only for the duration of the session
- All authentication happens server-side
- Client Secret is never exposed to the browser
Currently, testing is manual. Test by:
- Running queries against your Xero organization
- Verifying data accuracy
- Testing CSV export functionality
yarn buildThis creates an optimized production build in .next/.
yarn startThis Next.js application can be deployed to:
- Vercel (recommended):
vercel deploy - Netlify: Configure build command and output directory
- Self-hosted: Use
yarn build && yarn start - Docker: Create Dockerfile with Node.js and Next.js
Set these in your hosting platform:
NEXTAUTH_URL=https://yourdomain.com
- Verify credentials are correct in your Xero developer console
- Ensure you're using a Custom Connection (not OAuth 2.0)
- Check that your organization is added to the app
- Check your query filters in
scenarios.json - Verify the Xero endpoint is correct
- Ensure your organization has data matching the query
- These have been resolved in the latest version
- If you encounter them, ensure you're using consistent date/number formatting
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Commit your changes:
git commit -am 'Add feature' - Push to the branch:
git push origin feature-name - Submit a pull request
MIT License - see LICENSE file for details
For issues or questions:
- Open an issue on GitHub
- Check the Query Configuration Guide
- Review Xero API documentation
- Built with Next.js
- UI components from shadcn/ui
- Styled with Tailwind CSS
- Powered by Xero API v2.0
Built by an Excel geek who got tired of clicking. 🧮