Stitchflow
Xero logo

Xero User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 17, 2026

Summary and recommendation

Xero's Accounting API exposes a read-only GET /Users endpoint under the base URL https://api.xero.com/api.xro/2.0.

Authentication uses OAuth 2.0 Authorization Code flow with PKCE;

the accounting.settings.read scope is sufficient for read access to user records.

Every request must include a Xero-Tenant-Id header populated from a prior GET https://api.xero.com/connections call - requests without this header will fail.

Access tokens expire after 30 minutes;

the offline_access scope must be requested to receive a refresh token for long-lived integrations.

Rate limits are enforced at 60 calls per minute and 5,000 calls per day per app per tenant;

HTTP 429 is returned on breach, and the X-Rate-Limit-Problem header distinguishes whether the minute or day limit was hit.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (Authorization Code flow with PKCE supported)
Base URLOfficial docs
SCIM availableNo

Authentication

Auth method: OAuth 2.0 (Authorization Code flow with PKCE supported)

Setup steps

  1. Register an application at developer.xero.com/myapps to obtain a Client ID and Client Secret.
  2. Configure redirect URIs and select required OAuth 2.0 scopes in the app settings.
  3. Redirect the user to https://login.xero.com/identity/connect/authorize with response_type=code, client_id, redirect_uri, scope, and state parameters.
  4. Exchange the returned authorization code for an access token via POST to https://identity.xero.com/connect/token.
  5. Include the access token as a Bearer token in the Authorization header on all API requests.
  6. Use the refresh token to obtain new access tokens before expiry (access tokens expire after 30 minutes).
  7. For multi-org apps, retrieve the list of connected tenants via GET https://api.xero.com/connections and pass the target Xero-Tenant-Id header on each request.

Required scopes

Scope Description Required for
openid Required for OpenID Connect authentication; returns an ID token. Authentication
profile Returns basic profile information about the authenticated user. Identifying the authenticated user
email Returns the email address of the authenticated user. Identifying the authenticated user
accounting.settings Read/write access to organisation settings. GET /Users (read organisation users)
accounting.settings.read Read-only access to organisation settings including users. GET /Users (read-only)
offline_access Issues a refresh token to allow long-lived API access without re-authentication. Maintaining persistent API sessions

User object / data model

Field Type Description On create On update Notes
UserID string (GUID) Unique Xero identifier for the user. not applicable not applicable Read-only; assigned by Xero.
EmailAddress string Email address of the user. not applicable not applicable Read-only via API.
FirstName string First name of the user. not applicable not applicable Read-only via API.
LastName string Last name of the user. not applicable not applicable Read-only via API.
UpdatedDateUTC string (datetime) UTC timestamp of the last update to the user record. not applicable not applicable Read-only; Xero-managed.
IsSubscriber boolean Indicates whether the user is the Xero subscriber (account owner). not applicable not applicable Read-only.
OrganisationRole string (enum) Role of the user within the organisation. Values: READONLY, INVOICEONLY, STANDARD, ADVISER, MANAGEDCLIENT, CASHBOOKCLIENT. not applicable not applicable Read-only via API; role changes must be made in the Xero UI.

Core endpoints

List all users in an organisation

  • Method: GET
  • URL: https://api.xero.com/api.xro/2.0/Users
  • Watch out for: This endpoint is read-only. You cannot create, update, or delete users via the Accounting API. User management must be performed through the Xero web UI.

Request example

GET /api.xro/2.0/Users HTTP/1.1
Host: api.xero.com
Authorization: Bearer {access_token}
Xero-Tenant-Id: {tenant_id}
Accept: application/json

Response example

{
  "Users": [
    {
      "UserID": "d1164823-0ac1-41ad-987b-b4e30be0b9d0",
      "EmailAddress": "jane@example.com",
      "FirstName": "Jane",
      "LastName": "Smith",
      "UpdatedDateUTC": "/Date(1619000000000+0000)/",
      "IsSubscriber": true,
      "OrganisationRole": "ADVISER"
    }
  ]
}

Get a single user by UserID

  • Method: GET
  • URL: https://api.xero.com/api.xro/2.0/Users/{UserID}
  • Watch out for: Returns a single-element Users array even for a specific UserID lookup.

Request example

GET /api.xro/2.0/Users/d1164823-0ac1-41ad-987b-b4e30be0b9d0 HTTP/1.1
Host: api.xero.com
Authorization: Bearer {access_token}
Xero-Tenant-Id: {tenant_id}
Accept: application/json

Response example

{
  "Users": [
    {
      "UserID": "d1164823-0ac1-41ad-987b-b4e30be0b9d0",
      "EmailAddress": "jane@example.com",
      "FirstName": "Jane",
      "LastName": "Smith",
      "IsSubscriber": true,
      "OrganisationRole": "ADVISER"
    }
  ]
}

Get authenticated user identity (OpenID Connect)

  • Method: GET
  • URL: https://api.xero.com/api.xro/2.0/Users (authenticated user context via /identity/connect/userinfo)
  • Watch out for: This is the OIDC userinfo endpoint, not the Accounting API Users endpoint. Requires openid + profile + email scopes.

Request example

GET /identity/connect/userinfo HTTP/1.1
Host: api.xero.com
Authorization: Bearer {access_token}

Response example

{
  "sub": "abcd1234-...",
  "email": "user@example.com",
  "given_name": "Jane",
  "family_name": "Smith",
  "xero_userid": "d1164823-..."
}

List connected tenants (organisations)

  • Method: GET
  • URL: https://api.xero.com/connections
  • Watch out for: The tenantId from this response must be passed as the Xero-Tenant-Id header on all Accounting API calls.

Request example

GET /connections HTTP/1.1
Host: api.xero.com
Authorization: Bearer {access_token}

Response example

[
  {
    "id": "7369d8a3-...",
    "tenantId": "b2c3d4e5-...",
    "tenantType": "ORGANISATION",
    "tenantName": "Demo Company (Global)",
    "createdDateUtc": "2023-01-15T10:00:00"
  }
]

Rate limits, pagination, and events

  • Rate limits: Xero enforces per-app, per-tenant minute-level and daily limits on the Accounting API.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: When the per-minute limit is exceeded, Xero returns HTTP 429. The response includes an X-Rate-Limit-Problem header indicating whether the minute or day limit was hit. The X-MinLimit-Remaining and X-DayLimit-Remaining headers report remaining quota. Xero docs do not document a Retry-After header.
  • Pagination method: offset
  • Default page size: 100
  • Max page size: 100
  • Pagination pointer: page
Plan Limit Concurrent
All apps (standard) 60 API calls per minute per app per tenant; 5,000 API calls per day per app per tenant 0
  • Webhooks available: Yes
  • Webhook notes: Xero supports webhooks for a defined set of Accounting API object events. Webhooks are configured per-app in the Xero developer portal. Xero sends a signed POST payload to the registered endpoint; the receiver must respond with HTTP 200 within 5 seconds. Xero uses an intent-to-receive verification handshake on setup.
  • Alternative event strategy: User-specific events (user added, user role changed) are not available as webhook events. Polling GET /Users with the If-Modified-Since header is the only programmatic way to detect user changes.
  • Webhook events: Account.Created, Account.Updated, Contact.Created, Contact.Updated, Invoice.Created, Invoice.Updated, CreditNote.Created, CreditNote.Updated, PurchaseOrder.Created, PurchaseOrder.Updated

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Not documented
  • Endpoint: Not documented

Limitations:

  • Xero does not offer a native SCIM 2.0 endpoint on any plan.
  • No native SAML SSO is available; SSO is only available via third-party identity providers (e.g., miniOrange, AuthDigital) acting as middleware.
  • User provisioning and deprovisioning cannot be automated via any official Xero API; it must be performed through the Xero web UI.
  • Feature requests for SCIM and native SSO have been open in the Xero community for many years without an official release date.

Common scenarios

The primary automation scenario is building an identity graph of Xero users and their roles across one or more tenants.

Call GET /connections to enumerate tenants, then GET /Users?page=N per tenant - 100 users per page, offset pagination - to retrieve UserID, EmailAddress, FirstName, LastName, OrganisationRole, IsSubscriber, and UpdatedDateUTC.

Store UpdatedDateUTC per user record and pass it as the If-Modified-Since header on subsequent polling runs to retrieve only changed records;

this is the only available mechanism for detecting user lifecycle changes, as Xero's webhook event catalog does not include user-scoped events such as add, remove, or role change.

To resolve the identity of the OAuth-authenticated user, call GET https://api.xero.com/identity/connect/userinfo with openid, profile, and email scopes;

the xero_userid field in that response maps directly to UserID in the Accounting API, but OrganisationRole must be fetched per tenant separately since a user may hold different roles across organisations.

Audit all users and roles in a Xero organisation

  1. Complete OAuth 2.0 Authorization Code flow with scopes: openid profile email accounting.settings.read offline_access.
  2. Call GET https://api.xero.com/connections to retrieve the tenantId for the target organisation.
  3. Call GET https://api.xero.com/api.xro/2.0/Users with Xero-Tenant-Id header set.
  4. Iterate the returned Users array; record UserID, EmailAddress, FirstName, LastName, OrganisationRole, and IsSubscriber.
  5. Store UpdatedDateUTC per user to enable incremental polling using the If-Modified-Since header on subsequent runs.

Watch out for: Results are limited to 100 users per page. Use the page query parameter (?page=2, etc.) to retrieve additional pages if the organisation has more than 100 users.

Detect user changes via polling

  1. On first run, call GET /Users and record the latest UpdatedDateUTC across all returned users.
  2. On subsequent runs, call GET /Users with header If-Modified-Since: {last_recorded_datetime} in RFC 1123 format.
  3. Compare the returned user list against the previously stored snapshot to identify added, removed, or role-changed users.
  4. Alert or sync downstream systems based on detected differences.

Watch out for: Xero does not provide webhook events for user changes, so polling is the only available mechanism. The If-Modified-Since filter applies to the UpdatedDateUTC field on user records.

Identify the authenticated user after OAuth login

  1. Complete OAuth 2.0 flow with scopes: openid profile email.
  2. Call GET https://api.xero.com/identity/connect/userinfo with the Bearer access token.
  3. Extract sub (Xero user GUID), email, given_name, family_name, and xero_userid from the response.
  4. Optionally call GET /connections to list all organisations the authenticated user has authorised, then GET /Users/{UserID} per tenant to retrieve their OrganisationRole in each.

Watch out for: The xero_userid in the OIDC userinfo response corresponds to the UserID field in the Accounting API /Users endpoint, but the user may have different OrganisationRole values across different tenants.

Why building this yourself is a trap

The critical caveat for any integration targeting Xero user management is that the Accounting API /Users endpoint is strictly read-only - there is no API method to invite, provision, update roles for, or remove users from a Xero organisation on any current plan tier.

Xero also offers no native SCIM 2.0 endpoint and no native SAML SSO; the OrganisationRole field returned by the API cannot be written back via any official endpoint. Any workflow that requires write operations against Xero's user model must route through the web UI at Settings > Users or through third-party middleware.

Integrations that assume bidirectional user lifecycle control based on the presence of a /Users endpoint will hit this wall immediately.

Automate Xero workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 17, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste