{"id":12688,"library":"xero-node","title":"Xero Node.js SDK (OAuth 2.0 Client)","description":"The xero-node SDK provides a comprehensive client for interacting with the Xero APIs (Accounting, Assets, Bankfeeds, Files, Projects, Payroll AU/NZ/UK) using Node.js. It facilitates OAuth 2.0 authentication and API requests, simplifying integration for developers building applications that connect with Xero accounting data. The library is currently in its 15.0.0 major version, with frequent updates (multiple minor/patch releases per month, and major releases roughly every few months) to reflect changes in the underlying Xero API and add new features. Key differentiators include full API coverage across multiple Xero API sets and robust TypeScript support, making it suitable for enterprise-grade integrations requiring strong typing and reliability.","status":"active","version":"15.0.0","language":"javascript","source_language":"en","source_url":"git://github.com/XeroAPI/xero-node","tags":["javascript","typescript"],"install":[{"cmd":"npm install xero-node","lang":"bash","label":"npm"},{"cmd":"yarn add xero-node","lang":"bash","label":"yarn"},{"cmd":"pnpm add xero-node","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The library primarily uses ES Modules and ships with TypeScript types. CommonJS `require` should be avoided.","wrong":"const XeroClient = require('xero-node');","symbol":"XeroClient","correct":"import { XeroClient } from 'xero-node';"},{"note":"Individual API clients like `AccountingApi`, `PayrollAuApi`, etc., are named exports. Do not confuse with the top-level 'Accounting' concept.","wrong":"import { Accounting } from 'xero-node';","symbol":"AccountingApi","correct":"import { AccountingApi } from 'xero-node';"},{"note":"Specific API models (like `Contact`, `Invoice`, `BankTransaction`) are deeply nested and must be imported from their precise paths, typically within `xero-node/dist/gen/model/<api-set>/<model-name>`.","wrong":"import { Contact } from 'xero-node';","symbol":"Contact","correct":"import { Contact } from 'xero-node/dist/gen/model/accounting/contact';"},{"note":"Configuration interface for the XeroClient, useful for TypeScript users. Often confused with a class.","wrong":"import { XeroClientConfig } from 'xero-node';","symbol":"IXeroClientConfig","correct":"import { IXeroClientConfig } from 'xero-node';"}],"quickstart":{"code":"import { XeroClient, Contact, Contacts } from 'xero-node';\nimport { Response } from 'node-fetch';\n\nconst client_id = process.env.XERO_CLIENT_ID ?? '';\nconst client_secret = process.env.XERO_CLIENT_SECRET ?? '';\nconst redirect_uri = process.env.XERO_REDIRECT_URI ?? 'http://localhost:3000/callback';\nconst scopes = ['accounting.contacts.read', 'offline_access'];\n\nconst xero = new XeroClient({\n  clientId: client_id,\n  clientSecret: client_secret,\n  redirectUris: [redirect_uri],\n  scopes: scopes,\n});\n\nasync function authenticateAndFetchContacts() {\n  if (!client_id || !client_secret) {\n    console.error('XERO_CLIENT_ID and XERO_CLIENT_SECRET environment variables must be set.');\n    return;\n  }\n\n  // In a real application, you would store and retrieve tokens securely.\n  // This is a simplified example to get a token via a mock code (requires a valid initial auth flow).\n  // For a real-world scenario, you'd perform the OAuth2 dance.\n  // For demonstration purposes, we assume a refresh token is available or use a pre-authorized token.\n  \n  // Example of setting a token from a previous authorization (replace with actual token management)\n  xero.setTokenSet({\n    access_token: process.env.XERO_ACCESS_TOKEN ?? 'YOUR_INITIAL_ACCESS_TOKEN',\n    refresh_token: process.env.XERO_REFRESH_TOKEN ?? 'YOUR_INITIAL_REFRESH_TOKEN',\n    expires_at: Date.now() / 1000 + 3600 // Example: expires in 1 hour\n  });\n\n  try {\n    // Ensure token is fresh\n    if (await xero.apiClient.checkTokenSet() && xero.apiClient.tokenSet.expired()) {\n        console.log('Access token expired, attempting to refresh...');\n        await xero.apiClient.refreshToken();\n        console.log('Token refreshed successfully.');\n    }\n\n    console.log('Fetching contacts...');\n    const contactsResponse = await xero.accountingApi.getContacts(xero.tenantIds[0]);\n    const contacts = contactsResponse.body.contacts;\n    \n    if (contacts && contacts.length > 0) {\n      console.log(`Found ${contacts.length} contacts. First contact: ${contacts[0].name}`);\n    } else {\n      console.log('No contacts found.');\n    }\n  } catch (error) {\n    console.error('Error fetching contacts:', error);\n    if (error instanceof Response) {\n      const errorBody = await error.text();\n      console.error('API Error Response:', errorBody);\n    }\n  }\n}\n\nauthenticateAndFetchContacts();\n","lang":"typescript","description":"Demonstrates initializing the XeroClient, managing an access token (refreshing if expired), and making a basic API call to fetch contacts. Requires valid Xero API credentials and an existing tenant ID."},"warnings":[{"fix":"Review your `Employee` and `Employment` object payloads for Payroll NZ and UK. Ensure all newly required fields are populated before making API calls. Refer to the official Xero API documentation for the exact schema.","message":"Version 13.0.0 introduced breaking changes for Payroll NZ and UK API calls. Specifically, several fields in the `Employee` and `Employment` models (e.g., `firstName`, `lastName`, `dateOfBirth`, `startDate`, `payrollCalendarID`) became required. Calls made with older data models will fail.","severity":"breaking","affected_versions":">=13.0.0"},{"fix":"Migrate your code to use the supported alternatives for employee management. Consult the Xero API documentation or the Payroll API clients (e.g., `PayrollAuApi`, `PayrollNzApi`, `PayrollUkApi`) for the correct methods.","message":"As of version 13.1.0, several Accounting API endpoints related to `EmployeesAsync` have been marked obsolete and will be removed in future releases. These include `CreateEmployeesAsync`, `GetEmployeeAsync`, `GetEmployeesAsync`, and `UpdateOrCreateEmployeesAsync` variants.","severity":"deprecated","affected_versions":">=13.1.0"},{"fix":"When calling `xero.filesApi.getFiles()`, ensure you provide the `direction` parameter, typically with a value like `'asc'` or `'desc'` to specify sorting order.","message":"Version 12.0.0 introduced a breaking change by adding a new required query parameter, `direction`, to the `getFiles` endpoint. Calls to `getFiles` without this parameter will no longer work.","severity":"breaking","affected_versions":">=12.0.0"},{"fix":"Implement robust token storage and refresh logic. The `xero-node` client provides `setTokenSet` and `refreshToken` methods. You should store the `refresh_token` securely and use it to obtain new access tokens when the current one expires.","message":"Xero's OAuth 2.0 flow requires careful management of access tokens and refresh tokens. Access tokens are short-lived (30 minutes) and refresh tokens are valid for 60 days. Failing to refresh an expired access token or properly store/retrieve tokens will lead to authentication failures.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"After successful authentication, the `xeroClient.tenantIds` array will contain the IDs of the organizations the user has granted access to. Always pass the relevant `tenantId` (e.g., `xeroClient.tenantIds[0]`) as the first argument to API methods like `getContacts`, `createInvoices`, etc.","message":"The Xero API expects a `tenantId` (also known as `Xero-Tenant-Id` header) for most operations after authentication. This ID identifies the specific Xero organization you are interacting with. Forgetting to pass it or using an incorrect ID will result in errors.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure you have completed the OAuth 2.0 authentication flow correctly, retrieved a valid access token, and are setting it on the `XeroClient`. Implement token refresh logic to get a new access token using your refresh token before making requests with an expired token.","cause":"The access token used for the API request is either missing, invalid, or expired.","error":"Error: 401 Unauthorized"},{"fix":"Verify that your request body adheres to the Xero API schema for the specific endpoint. Ensure all required fields for the entity type are present and correctly formatted. For example, when creating a contact, `contacts: [{ name: '...' }]` is expected.","cause":"You are attempting to create or update an entity (e.g., Contacts, Invoices) but the request body is missing or malformed, or required fields within the entity object are not provided.","error":"Error: 400 Bad Request - 'The Contacts field is required.' (or similar for other entities)"},{"fix":"Explicitly cast your object to the correct type (e.g., `const newContact: Contact = { name: 'Test Contact' };`) or ensure your object strictly matches the model's interface, including all required properties as defined in `xero-node/dist/gen/model/accounting/contact.d.ts`.","cause":"This TypeScript error occurs when you pass a plain JavaScript object that doesn't fully conform to the `Contact` (or other model) interface expected by the SDK method.","error":"Error: Argument of type '{ name: string; }' is not assignable to parameter of type 'Contact'."}],"ecosystem":"npm"}