> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chift.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Xero

export const OverviewLegend = ({showTitle = true}) => <blockquote>
    {showTitle && <p>
        <strong>Overview legend 🧭</strong>
      </p>}
    <table>
      <thead>
        <tr>
          <th>Column</th>
          <th>Value</th>
          <th>Meaning</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Geography</td>
          <td>🇫🇷 FR · 🇧🇪 BE · …</td>
          <td>Countries where the connector is officially supported.</td>
        </tr>
        <tr>
          <td rowSpan={3}>Software type</td>
          <td>SaaS</td>
          <td>Through API.</td>
        </tr>
        <tr>
          <td>On-premise (local agent)</td>
          <td>
            Software running locally — installation of a local agent is
            required.
          </td>
        </tr>
        <tr>
          <td>On-premise (API)</td>
          <td>Software running locally — API available.</td>
        </tr>
        <tr>
          <td rowSpan={2}>Status</td>
          <td>🔵 Live</td>
          <td>Connector is generally available and production-ready.</td>
        </tr>
        <tr>
          <td>🟣 Beta</td>
          <td>
            Connector is in beta — usable in production but may still evolve.
          </td>
        </tr>
        <tr>
          <td>Multi folder</td>
          <td>✅ Yes / ❌ No</td>
          <td>
            Connection to multiple accounting folders at the same time (see{' '}
            <a href="/developer-guides/api-guides/accounting/folders">
              accounting folders guide
            </a>
            ).
          </td>
        </tr>
        <tr>
          <td>Rate limits</td>
          <td>✅ No / ❌ Yes</td>
          <td>Whether the target software sets rate limits on API calls.</td>
        </tr>
        <tr>
          <td rowSpan={3}>API keys</td>
          <td>❎ No</td>
          <td>
            No API keys required (OAuth2 client secret and client ID). No
            requirements to activate the connector — you can create a
            connection.
          </td>
        </tr>
        <tr>
          <td>🔑</td>
          <td>
            Keys are required to activate the connector. Chift cannot act as
            intermediary to obtain them; we can still assist with steps to get
            keys directly from the software provider.
          </td>
        </tr>
        <tr>
          <td>🔑 ☑️ via Chift</td>
          <td>
            Keys are required to activate the connector. You can go through
            Chift to get the keys (intermediary or partnership keys). An
            approval process may still apply, but you do not need to request
            keys from the vendor on your own.
          </td>
        </tr>
        <tr>
          <td rowSpan={3}>Approval / certification process</td>
          <td>⚡ Instant</td>
          <td>
            No keys required, or Chift can encode their keys for you when
            requested. Activation is instantaneous.
          </td>
        </tr>
        <tr>
          <td>🟢 Approval — [Duration]</td>
          <td>
            Approval will be granted. The vendor may require information or app
            configuration in a developer portal before issuing keys.
          </td>
        </tr>
        <tr>
          <td>🟠 Approval — [Duration]</td>
          <td>
            Approval is not guaranteed — often due to integration strategy; the
            vendor may do a deeper assessment (competitors, partnership
            requirements, and similar).
          </td>
        </tr>
        <tr>
          <td rowSpan={2}>Activation time</td>
          <td>⚡ Instant</td>
          <td>
            If no keys are required, or Chift has keys ready to share with you.
          </td>
        </tr>
        <tr>
          <td>Time</td>
          <td>
            Estimated time to get the connector activated in production as a
            result of the approval or certification process (e.g. ⏱️ 2 days, 1
            week).
          </td>
        </tr>
        <tr>
          <td rowSpan={3}>Extra fees — software editor</td>
          <td>❎ No</td>
          <td>No fees charged by the software editor.</td>
        </tr>
        <tr>
          <td>💰 🕹️</td>
          <td>Fees associated with obtaining a testing account.</td>
        </tr>
        <tr>
          <td>💰 🔑</td>
          <td>Fees charged to get API keys.</td>
        </tr>
        <tr>
          <td rowSpan={2}>Extra fees — end user</td>
          <td>❎ No</td>
          <td>The end user does not pay extra to get integrated.</td>
        </tr>
        <tr>
          <td>💰 Yes</td>
          <td>The end user must pay extra to get integrated.</td>
        </tr>
        <tr>
          <td>Comments on costs</td>
          <td>—</td>
          <td>Additional notes on fees or pricing when relevant.</td>
        </tr>
        <tr>
          <td rowSpan={4}>Sandbox account</td>
          <td>✅ via Chift</td>
          <td>Chift can provide you with a sandbox.</td>
        </tr>
        <tr>
          <td>🟠 Only through integrator</td>
          <td>Only the software's integrator can provide a sandbox.</td>
        </tr>
        <tr>
          <td>✅ Self-service</td>
          <td>You can create your own sandbox.</td>
        </tr>
        <tr>
          <td>✅ Trial account</td>
          <td>It is possible to create a trial account.</td>
        </tr>
      </tbody>
    </table>
  </blockquote>;

export const ConnectorCardIframe = ({api = 'accounting', connectors}) => {
  const [theme, setTheme] = React.useState('light');
  React.useEffect(() => {
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const queryParams = new URLSearchParams({
    api,
    theme,
    ...connectors ? {
      connectors
    } : {}
  });
  const iframeUrl = `https://chift-coverage-matrix.s3.eu-west-3.amazonaws.com/connector-card.html?${queryParams.toString()}`;
  return <iframe src={iframeUrl} title={`Chift connector information - ${api}`} style={{
    display: 'block',
    width: '100%',
    height: '480px',
    margin: 0,
    padding: 0,
    border: 'none'
  }} />;
};

export const CoverageIframe = ({api = 'accounting', connectors}) => {
  const [theme, setTheme] = React.useState('light');
  const [isFullscreen, setIsFullscreen] = React.useState(false);
  const [currentIframeUrl, setCurrentIframeUrl] = React.useState(null);
  const iframeRef = React.useRef(null);
  React.useEffect(() => {
    const checkTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setTheme(isDark ? 'dark' : 'light');
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  React.useEffect(() => {
    const handleFullscreenChange = () => {
      setIsFullscreen(!!document.fullscreenElement);
    };
    document.addEventListener('fullscreenchange', handleFullscreenChange);
    return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
  }, []);
  React.useEffect(() => {
    const handleMessage = event => {
      if (!event.origin.includes('chift-coverage-matrix.s3.eu-west-3.amazonaws.com')) return;
      if (event.data?.type === 'urlChange' && event.data?.url) {
        setCurrentIframeUrl(event.data.url);
      }
    };
    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);
  const queryParams = new URLSearchParams({
    api,
    theme,
    ...connectors ? {
      connectors
    } : {}
  });
  const iframeUrl = `https://chift-coverage-matrix.s3.eu-west-3.amazonaws.com/coverage.html?${queryParams.toString()}`;
  const openUrl = currentIframeUrl || iframeUrl;
  const toggleFullscreen = () => {
    if (!document.fullscreenElement) {
      iframeRef.current?.requestFullscreen();
    } else {
      document.exitFullscreen();
    }
  };
  const isDark = theme === 'dark';
  const buttonStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: '8px',
    padding: '4px 12px',
    fontSize: '14px',
    fontWeight: '500',
    color: isDark ? '#d4d4d4' : '#374151',
    backgroundColor: 'transparent',
    border: `1px solid ${isDark ? '#404040' : '#e5e7eb'}`,
    borderRadius: '12px',
    cursor: 'pointer',
    textDecoration: 'none',
    transition: 'all 0.15s ease'
  };
  const hoverBg = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.03)';
  const hoverBorder = isDark ? '#525252' : '#d1d5db';
  const defaultBg = 'transparent';
  const defaultBorder = isDark ? '#404040' : '#e5e7eb';
  return <>
  <div style={{
    display: 'flex',
    justifyContent: 'flex-end',
    gap: '12px',
    marginBottom: '8px'
  }}>
    <button onClick={toggleFullscreen} style={buttonStyle} onMouseEnter={e => {
    e.target.style.backgroundColor = hoverBg;
    e.target.style.borderColor = hoverBorder;
  }} onMouseLeave={e => {
    e.target.style.backgroundColor = defaultBg;
    e.target.style.borderColor = defaultBorder;
  }}>
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        {isFullscreen ? <>
            <polyline points="4 14 10 14 10 20"></polyline>
            <polyline points="20 10 14 10 14 4"></polyline>
            <line x1="14" y1="10" x2="21" y2="3"></line>
            <line x1="3" y1="21" x2="10" y2="14"></line>
          </> : <>
            <polyline points="15 3 21 3 21 9"></polyline>
            <polyline points="9 21 3 21 3 15"></polyline>
            <line x1="21" y1="3" x2="14" y2="10"></line>
            <line x1="3" y1="21" x2="10" y2="14"></line>
          </>}
      </svg>
      {isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
    </button>
    <a href={iframeUrl} target="_blank" rel="noopener noreferrer" style={buttonStyle} onMouseEnter={e => {
    e.target.style.backgroundColor = hoverBg;
    e.target.style.borderColor = hoverBorder;
  }} onMouseLeave={e => {
    e.target.style.backgroundColor = defaultBg;
    e.target.style.borderColor = defaultBorder;
  }}>
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
        <polyline points="15 3 21 3 21 9"></polyline>
        <line x1="10" y1="14" x2="21" y2="3"></line>
      </svg>
      Open in new tab
    </a>
  </div>
  <iframe ref={iframeRef} src={iframeUrl} title={`Chift Coverage Matrix - ${api}`} style={{
    height: 'max(500px, 80vh)'
  }} className="w-full" allowFullScreen />
  <blockquote>
    <p>
      <strong>Matrix Legend 🧭</strong>
    </p>
    <table>
      <thead>
        <tr>
          <th>Status</th>
          <th>Meaning</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>✅ Implemented</td>
          <td>Endpoint is implemented and available.</td>
        </tr>
        <tr>
          <td>❌ Not supported</td>
          <td>
            Endpoint is not supported by the target software (connector
            limitation). Cannot be implemented.
          </td>
        </tr>
        <tr>
          <td>💬 On request</td>
          <td>
            Endpoint is not implemented but feasibility is validated. Can be
            implemented on request — contact your Chift point of contact to
            discuss scope and timing.
          </td>
        </tr>
        <tr>
          <td>🔎 To be analyzed</td>
          <td>
            Endpoint is not implemented and feasibility has not yet been fully
            assessed. Analysis is pending.
          </td>
        </tr>
      </tbody>
    </table>
  </blockquote>
</>;
};

<ConnectorCardIframe api="accounting" connectors="Xero" />

<Accordion title="Overview Legend 🧭">
  <OverviewLegend showTitle={false} />
</Accordion>

## Introduction

Xero is a cloud-based accounting platform for small and medium-sized businesses. It provides real-time financial visibility, automated bank reconciliation, and comprehensive reporting tools to help businesses streamline accounting processes and make informed decisions.

Our Xero connector allows your application to securely integrate with your users’ Xero accounts via OAuth2.

<Warning>
  **Important:** Since March 2, 2026, Xero introduced a new tiered API pricing system and additional limitations affecting authentication, API usage, and feature access. These changes have direct impact on integration design, data consumption, and costs.
</Warning>

## Configure Xero

### Prerequisites

* Xero account with administrator permissions
* OAuth2 application in Xero with **Client ID** and **Client Secret**
* Appropriate OAuth scopes and pricing plan for the data you plan to access
* Note: Uncertified apps are now limited to **5 connected organizations**

### Activation process

**Setting up your Xero App**

1. If you don’t already have a Xero account, you can create one here: [Free trial](https://www.xero.com/signup/)
2. Sign in to the [Xero Developer Portal](https://developer.xero.com/).
3. Create a new app in the developer console:

* **App name:** Choose a clear name that users will recognize during the OAuth flow.
* **Integration type:** Select **Web app**.
* **Company or application URL:** Enter your company’s website (starting with `https://`).
* **Redirect URI:** Set to `https://chift.app/oauth2/redirect`.

4. Retrieve your app credentials and configure them in Chift:

* In the Xero Developer Portal, open **My Apps** and select your application.
* Reveal your **Client ID** from the configuration panel.
* Click **Generate secret** to create a **Client Secret**.
* Copy both values and store them securely.

Enter the Client ID and Client Secret in the Xero connector configuration inside the Chift platform.

<Accordion title="Scopes">
  Scopes are not set when creating the Xero Web app. They can be configured in the Chift Xero connector and requested during the OAuth flow.\
  \
  For SSO only (not related to accounting operations), you have to request `openid`, `profile`, `email`.

  Some scopes are marked as *deprecated* and must not be requested for new apps created after 02/03/2026; doing so returns an invalid scope error. They remain listed only for backward compatibility and will be removed in the future. See also [Xero’s organisation scopes](https://developer.xero.com/documentation/guides/oauth2/scopes/#organisation-scopes).

  To use the `accounting.journals.read` scope, you need approval from Xero.

  | Scope                                   | Status        | Purpose                                                                                                     | Optional |
  | :-------------------------------------- | :------------ | :---------------------------------------------------------------------------------------------------------- | :------: |
  | `offline_access`                        |               | Enables long-lived refresh tokens for background syncing                                                    |          |
  | `accounting.transactions.read`          | Deprecated    | Read invoices, credit notes, payments, manual journals                                                      |     ✅    |
  | `accounting.invoices.read`              | New           | Read invoices and credit notes                                                                              |     ✅    |
  | `accounting.invoices`                   | New           | Create and update invoices and credit notes                                                                 |     ✅    |
  | `accounting.payments.read`              | New           | Read payments                                                                                               |     ✅    |
  | `accounting.payments`                   | New           | Create and update payments                                                                                  |     ✅    |
  | `accounting.banktransactions.read`      | New           | Read bank transactions                                                                                      |     ✅    |
  | `accounting.manualjournals.read`        | New           | Read manual journals                                                                                        |     ✅    |
  | `accounting.manualjournals`             | New           | Create and update manual journals                                                                           |     ✅    |
  | `accounting.reports.trialbalance.read`  | New           | Read trial balance reports                                                                                  |     ✅    |
  | `accounting.journals.read`              | Upon approval | Read manual journal entries                                                                                 |     ✅    |
  | `accounting.settings.read`              |               | Read organisation settings and chart of accounts                                                            |          |
  | `accounting.reports.read`               | Deprecated    | Read accounting reports                                                                                     |     ✅    |
  | `accounting.contacts.read`              |               | Read customers and suppliers                                                                                |          |
  | `accounting.attachments.read`           |               | Read attachments on Invoices, CreditNotes, ManualJournals                                                   |          |
  | `accounting.budgets.read`               |               | Read budgets                                                                                                |          |
  | `accounting.reports.tenninetynine.read` |               | Read 1099 reports                                                                                           |     ✅    |
  | `files.read`                            |               | Read from the Xero Files library                                                                            |          |
  | `accounting.transactions`               | Deprecated    | Create/update invoices, credit notes, manual journals; for PDF upload combine with `accounting.attachments` |     ✅    |
  | `accounting.contacts`                   |               | Create/update customers and suppliers                                                                       |     ✅    |
  | `accounting.settings`                   |               | Create chart-of-accounts entries; manage organisation settings; create/update tracking options              |     ✅    |
  | `accounting.attachments`                |               | Upload attachments to Invoices, CreditNotes, ManualJournals                                                 |     ✅    |
  | `files`                                 |               | Write to the Xero Files library                                                                             |     ✅    |
</Accordion>

### ℹ️ Certification, Limits & Pricing (Updated 2026)

#### 1. Development and initial limits (Starter Tier)

Every new Xero integration begins in the **Development phase** under the **Starter Tier**. This is the default state for uncertified apps:

* **Connection Cap:** Limited to a maximum of **5 active connected organizations**.
* **API Limits:** 1,000 calls per day per organization; 60 calls per minute.
* **Upgrade Path:** To connect more than 5 organizations, you must upgrade to a paid tier (Core, Plus, or Advanced) and begin the certification process.

#### 2. Certification and growth (scaling beyond 5 connections)

To grow your app and remove the 5-connection limit, you must move through the **Certification process**. This transition turns your "integration" into a formal **Xero App Partnership**:

* **The "Beta" Threshold:** You need to reach at least **10 active connections** to qualify for App Store certification.
* **Upgrade Requirement:** To reach those 10 connections, you must upgrade from the free Starter plan to a paid tier (starting with the **Core Plan**).
* **Technical Review:** Xero will review your app to ensure it meets quality standards, including:
  * Proper error handling.
  * Secure data management.
  * A seamless onboarding flow (Sign In with Xero).
* **App Store Listing:** Once certified, your app becomes eligible for listing on the **Xero App Store**, providing global visibility to Xero’s millions of subscribers.

#### 3. Pricing considerations — API pricing and tiers (effective March 2, 2026)

Billing is based on your **Connected Companies** and **Monthly Data Consumption** (data downloaded via the API). The new model has five tiers: **Starter, Core, Plus, Advanced, Enterprise**.

| **Plan**       | **Max Connected Companies** | **Data Quota** | **Key Features & Notes**                        |
| :------------- | :-------------------------- | :------------- | :---------------------------------------------- |
| **Starter**    | 5                           | Unlimited\*    | **Free**; For dev & small-scale testing.        |
| **Core**       | 50                          | 10 GB          | \~€22/month; Threshold for Certification.       |
| **Plus**       | 1,000                       | 50 GB          | \~€150/month; High-volume scaling.              |
| **Advanced**   | 10,000                      | 250 GB         | \~€890/month; Unlocks premium APIs (see below). |
| **Enterprise** | Custom                      | Custom         | Custom contract; large-scale usage.             |

* **Data Overage:** Exceeding your quota costs approximately **€1.40 per additional GB**.
* **Premium APIs (higher tiers only):** The **Journals API** (`GET /Journals`), the **Xero Practice Manager (XPM) API**, and **Bulk Connections** are restricted to the higher tiers (Advanced and above) and additionally require a **security assessment** (initial + annual) and a **use case validation** by Xero.

<Note>
  **Journals vs Manual Journals — two different objects in Xero.**

  * **Journal** (`GET /Journals`) → [`GET Journal Entries`](/api-reference/endpoints/accounting/get-journal-entries) in Chift. **Read-only**, returns every general-ledger entry (including those auto-generated from invoices, payments, etc.). Gated as a **premium API**: requires the **Advanced** tier + security assessment.
  * **Manual Journal** (`POST /ManualJournals`) → [`POST Journal Entry`](/api-reference/endpoints/accounting/create-journal-entry) in Chift. **Write-only**, only covers entries you push manually. **Available on all tiers** — no Advanced required.

  The naming is counter-intuitive: **manual journals are not a subset of journals** — the two objects are unrelated. Advanced is only needed for the **read** endpoint; pushing manual journals works on every tier.
</Note>

<Info>
  Special Certification & Financial Services\
  If your application falls into any of the following categories, you operate under a **Specific Financial Services Contract** rather than the standard developer terms:

  * **Bank Feeds:** Automatically pushing transaction data into Xero.
  * **Lending & Credit:** Accessing Xero data specifically to assess creditworthiness or provide business loans.
  * **Expense Management:** Specialized flows for corporate cards and employee reimbursements.
  * **Payment Services:** Facilitating the movement of funds between bank accounts and Xero.
</Info>

### 🔗 Official Reference Links

For detailed guides on the certification journey and the latest pricing updates, consult:

* **Step-by-Step Certification Guide:** [Building and Growing your App](https://developer.xero.com/documentation/xero-app-store/app-partner-guides/building-and-growing-your-app)
* **Pricing & Policy FAQ:** [Xero API Updates 2026](https://developer.xero.com/faq/pricing-and-policy-updates)

***

## Test Xero

Xero offers a **demo company** you can use to test your integrations. It comes with sample data, allowing you to validate and experiment with your setup before connecting to real organizations. Once you’ve registered on the Developer Portal, you can follow [this guide](https://developer.xero.com/documentation/development-accounts/#accessing-the-xero-demo-company) to access the demo environment.

## Connect Xero

To activate a connection with Xero, users will have to go through the following steps:

* French article: [Help Center - Xero - FR](https://help.chift.app/articles/9332701143-xero?lang=fr)
* English article: [Help Center - Xero - EN](https://help.chift.app/articles/9332701143-xero?lang=en)

## Rate limits

Limits apply to API calls per tenant (organisation, account, or practice):

* **Concurrent:** 5 calls at a time
* **Per Minute:** 60 calls
* **Total App Minute**: 10,000 calls (Total across all connected tenants)
* **Daily:** 5,000 calls

Across all tenants, your app is limited to **10,000 calls per minute**.\
(See Xero’s [documentation](https://developer.xero.com/documentation/guides/oauth2/limits/#api-rate-limits) for more details.)

## Technical limitations / specificities

### Suppliers

In Xero, **all suppliers are contacts**. A contact is initially neutral. It is treated as a supplier **once a supplier invoice is linked to it**. Contacts can also act as customers simultaneously; the supplier role is **inferred from activity**, not stored as a separate entity.

## Coverage

<CoverageIframe api="accounting" connectors="Xero" />

## Troubleshooting

### Specific errors for Xero:

| Error Code                         | Error description                                                                                                                                | Resolution                                                                            |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| ERROR\_ACCOUNT\_TYPE\_NOT\_ALLOWED | Journal entries can only be linked to general ledger accounts in Xero.                                                                           | Use only general ledger accounts for journal entries.                                 |
| ERROR\_INVALID\_BALANCE\_RANGE     | Xero doesn't support the 'start' parameter in balance retrieval. Balances can only be retrieved YTD (from the start of the current fiscal year). | Use the start of the current fiscal year as the date parameter for balance retrieval. |
