Integrations
Xero SDK: get started
The complete Xero API through typed application or agent clients
Use an ordinary App, or an Agent when the workload needs agent identity. Alter builds provider requests from a reviewed contract and applies the same connection authorization, policy, approvals, and audit records as raw requests.
Install
Section titled “Install”npm install @alter-ai/sdk @alter-ai/xero# Python (installs core automatically)pip install alter-sdk-xeroThe provider package includes only Xero types and response schemas and pins its contract version.
Connect and call
Section titled “Connect and call”Configure Xero, request the scopes the application needs, and retain each customer’s grant ID after Connect consent. Alter supplies the credential; it never belongs in operation input.
A Xero grant can reach several organisations. identity.getConnections lists them, and every other call names one with the xero-tenant-id header. A policy rule can read that header, so a rule can limit a grant to one organisation.
import { App } from "@alter-ai/sdk";import { XeroIntegration } from "@alter-ai/xero";const app = new App({ apiKey: process.env.ALTER_API_KEY! });try { const client = new XeroIntegration(app, { grantId: process.env.XERO_GRANT_ID! }); const connections = await (await client.identity.getConnections({})).wait(); const [organisation] = connections.bodyJson() as { tenantId: string }[]; const invoices = await ( await client.accounting.getInvoices({ headers: { "xero-tenant-id": organisation.tenantId }, }) ).wait(); console.log(invoices.bodyJson());} finally { await app.close();}from alter_sdk import Appfrom alter_sdk_xero import XeroIntegration
async def first_call(api_key: str, grant_id: str): async with App(api_key=api_key) as app: client = XeroIntegration(app, {"grant_id": grant_id}) connections = await (await client.identity.get_connections({})).wait() tenant_id = connections.body_json()[0]["tenantId"] call = await client.accounting.get_invoices( {"headers": {"xero-tenant-id": tenant_id}} ) result = await call.wait() return result.body_json()Complete versioned contract
Section titled “Complete versioned contract”Contract 2026-10-31 holds 308 operations from Xero’s official OpenAPI documents at release 19.1.0: Accounting, Assets, Files, Projects, Connections and Australian payroll.
Not included:
- Payroll for New Zealand and the United Kingdom, and Australian payroll v2. They share the same routes under different schemas, so one contract cannot describe them together.
- Bank Feeds, Finance and Account Data. Their scopes need Xero certification, and Account Data is in beta.
- App Store billing. It needs a token for the app itself, not a customer’s grant.
DELETE /connections/{id}. It disconnects an organisation from the grant, and only new consent restores it. It is not an SDK method. A raw proxy call to it is classified asadmin,deleteandwrite, so a rule can govern it.
Expense claims and receipts are published, but Xero apps created on or after March 2026 cannot call them, because no granular scope covers them.
The API reference links every operation to its complete input fields, response models, TypeScript and Python examples, and policy identity. Browse by resource under this provider’s API reference in the Integrations sidebar, or download the pinned OpenAPI specification. The Developer Portal provider view and alter integrations operations xero --version 2026-10-31 also expose the schemas.
Generated calls pin the Alter contract version. Xero’s own product, role, scope and subscription requirements still apply. There is no silent version fallback and no automatic write retry or pagination.
alter integrations execute xero xero.identity_getconnections \ --version 2026-10-31 --grant "$XERO_GRANT_ID" --input '{}' --waitArguments use native path, query, headers, and body containers. Array query parameters are sent the way the provider’s document declares them: one delimited value where it declares explode: false (or a pipe- or space-delimited style), repeated keys otherwise. SDK methods preserve provider field names, decimal lexemes and large integers as strings when necessary. HTTP 204 returns null / None from the typed decoder. Binary responses use bodyBytes() / body_bytes(); the CLI can save them with --output-file.
Approvals
Section titled “Approvals”A policy rule can require approval for any operation. The call returns a pending approval; wait() resolves once the approver decides, and resume(approvalId) picks a pending approval up after a restart. Approval cards show the operation, its contract version and the request input the sensitivity review marked safe to display.
Uploads and approvals
Section titled “Uploads and approvals”Twenty-two attachment operations (accounting.create_invoice_attachment_by_file_name and its siblings) and two file operations (files.upload_file, files.upload_file_to_folder) upload a file. Every binary input is a JSON object with filename, content_type, and content_base64. An attachment goes to Xero as the file’s raw bytes under its content_type; a Files upload goes as multipart.
Uploaded file contents are classified sensitive. An upload a policy sends for approval is held with the pending approval, and the approver sees the file’s name and size, never its contents. The shared proxy accepts up to 5 MiB of serialized input and encoded upload data, and base64 adds overhead, so Xero’s 25 MB attachment limit is not reachable here.
Policy and sensitive data
Section titled “Policy and sensitive data”Every operation’s policy decision (its families, its sensitive request fields and the parameters a rule can read) was reviewed against Xero’s page for that operation. The per-operation families are in the Xero connection’s policy table.
readis only for an operation that changes nothing. Every other operation carrieswrite, and a removal also carriesdelete. Xero deletes and voids through aStatusfield, so a write that can set one carriesdeletetoo.paymentmarks anything that changes what is owed or how money moves: invoices, credit notes, payments, bank transactions, contacts’ payment details and every payroll write.sendmarksemail_invoiceand the repeating invoice writes, which can make Xero email a contact.adminmarks the chart of accounts, tax rates, currencies, tracking categories, branding payment services, pay items, payroll calendars and super funds.
Sensitive request fields are ordinary input: the call runs with no special permission. Under an approval rule each value is encrypted in Vault with the pending approval and shown masked. They are bank account numbers, BSBs, tax numbers, tax file numbers, dates of birth, super member numbers and uploaded files.
Payload capture follows the application’s capture setting, on managed and raw proxied calls alike: credentials are removed, every field Alter’s reviewed inventory for the provider marks sensitive is masked in requests and responses, including fields it locates only by operation, and binary bodies, calls that can return a credential and calls carrying raw card data are never stored. Metadata audit is unchanged.
No Xero operation returns a live credential, so none is refused by default. Alter’s connection holds the OAuth tokens and Xero’s documents publish no route that returns one.
Batch results
Section titled “Batch results”Batch writes accept summarizeErrors. With summarizeErrors=false Xero answers 200 and marks each failed item with StatusAttributeString ERROR or ValidationErrors. Alter marks that response as a provider fault, so result.ok is false and the typed decoder raises ProviderAPIError, but the items without an error were written. Read the body with bodyBytes() / body_bytes() and resend only the failed items.
Maintenance
Section titled “Maintenance”The pinned official documents, their hash, the generated methods and the policy mapping are checked together. Scheduled checks save upstream candidates and report additions, removals and schema changes; they never replace a published contract automatically. Existing contract versions remain available for installed SDKs and pending approvals.
Resume after a restart
Section titled “Resume after a restart”Persist call.approvalId (TypeScript) or call.approval_id (Python), then construct a fresh provider client and call resume(savedId), followed by wait(). The stored approval selects the operation; this installed package verifies its provider and contract version before attaching the response validator. Mismatches raise ManagedOperationError, never silently decode using another contract. See persist-and-resume examples.
Rules for SDK functions
Section titled “Rules for SDK functions”Policy operation fields also accept the published SDK function IDs listed by alter integrations operations xero. Use when.operation for a json_match or conditional require_approval rule, or match.operations for content_match (including redaction and step-up). Conditional approval can also use when.match.operations with parameter conditions. alter policy simulate --operation <function-id> tests that exact function.
Managed calls carry a server-verified function identity. Alter still evaluates policy against the actual method, URL and body after building the request from the authorized connection. Existing endpoint, method, host, catalog-operation and family rules continue to apply unchanged. A raw call can share an endpoint with several SDK functions and matches all of them; a function name never grants permission or bypasses existing restrictions.
For example, this rule requires approval for one function:
{ "effect": "require_approval", "when": { "operation": "xero.emailinvoice" }, "approval": { "approvers": ["reviewer@example.com"] }}Save it with alter policy rules create --app <app-id> --grant <grant-id> --type require_approval --body @rule.json, or select the function in the portal.