Xero ExecModule
Overview
XeroModule connects ValkyrAI workflows to the Xero Accounting API through the native map I/O ExecModule ABI. Workflow Studio discovers it as XeroModule; the OAuth access token and Xero tenant UUID resolve only from an encrypted IntegrationAccount.
The initial operation set covers the accounting path from organisation and contact to receivable and payment:
get_organisationlist_contacts,get_contact,create_contact,update_contactlist_invoices,get_invoice,create_invoice,update_invoice,email_invoicelist_payments,get_payment,create_payment
Xero list pagination is one-based and bounded. Only GET requests retry automatically. Contact, invoice, payment, and email writes run once so an ambiguous timeout cannot duplicate or resend financial activity.
Usage
- Create a Xero OAuth 2.0 application and authorize
offline_access,accounting.contacts,accounting.transactions, andaccounting.settingsas required by the selected operations. - Store the current OAuth access token in the encrypted
apiKeyfield of anIntegrationAccount. - Store the selected Xero connection tenant UUID in
IntegrationAccount.accountIdand set status toREADY. - Bind that account through
ExecModuleConfig.authConfig.integrationAccount. - Use
get_organisationto verify the intended tenant before financial mutations. - Use a stable
InvoiceNumber,Reference, or workflow correlation value when creating records. - Reconcile Xero state before repeating any write after an ambiguous connection failure.
Workflow input cannot provide an OAuth token, tenant ID, or arbitrary endpoint. The module routes only to the fixed https://api.xero.com/api.xro/2.0 host.
Inputs
| Name | Type | Requirement | Default | Description and constraints |
|---|---|---|---|---|
operation | string | Required | None | One of the 13 documented operations. |
resourceId | string | Get, update, and email operations | None | Canonical Xero entity UUID. |
contact | object or JSON string | Contact create/update | None | Allowlisted contact fields, 512 KiB maximum. |
invoice | object or JSON string | Invoice create/update | None | Allowlisted invoice fields and 1–1,000 line items, 512 KiB maximum. |
payment | object or JSON string | Payment create | None | Allowlisted payment fields with invoice and account UUID references. |
page | integer | Optional list continuation | 1 | One-based list page. |
limit | integer | Optional | 100 | Maximum emitted resources, 1–10,000. |
returnAll | boolean | Optional | false | Continue 100-record pages until exhaustion or the result cap. |
Contact payloads accept Name, ContactNumber, AccountNumber, ContactStatus, FirstName, LastName, EmailAddress, BankAccountDetails, TaxNumber, AccountsReceivableTaxType, AccountsPayableTaxType, Addresses, Phones, IsSupplier, IsCustomer, DefaultCurrency, SalesTrackingCategories, PurchasesTrackingCategories, and ContactPersons.
Invoice payloads accept Type, Contact, LineItems, Date, DueDate, LineAmountTypes, InvoiceNumber, Reference, BrandingThemeID, Url, CurrencyCode, Status, SentToContact, ExpectedPaymentDate, and PlannedPaymentDate. Type must be ACCREC or ACCPAY; Contact.ContactID must be a UUID.
Payment payloads accept Invoice, Account, Date, Amount, Reference, CurrencyRate, IsReconciled, Status, and PaymentType. Invoice.InvoiceID, Account.AccountID, and a positive Amount are required.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation name. |
attempts | integer | Always | Provider attempts across all pages. |
httpStatus | integer | Provider responded | Last Xero HTTP status. |
data | object | Single-resource/write success | Provider organisation, contact, invoice, payment, or email confirmation. |
ContactID, InvoiceID, PaymentID, OrganisationID | string | Provider returns field | Safe convenience identifiers. |
InvoiceNumber | string | Provider returns field | Xero invoice number. |
items / count | array / integer | List success | Bounded entity results and emitted count. |
hasMore | boolean | List success | Whether another numeric page may exist. |
nextPage | integer | Another page may exist | One-based continuation page. |
requestId | string | Xero returns one | Safe correlation or request trace reference. |
error | object | Failure | Safe {code, message, httpStatus?, retryable} details. |
OAuth tokens, tenant headers, and authorization headers never enter outputs. Provider messages pass through ValkyrAI's workflow sensitive-data policy.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Xero OAuth 2.0 application |
| Scopes | offline_access, accounting.contacts, accounting.transactions, and accounting.settings as needed |
accountId | Tenant UUID returned by the Xero Connections API |
apiKey | Current OAuth access token in an encrypted SecureField |
password | Legacy encrypted token fallback only; prefer apiKey |
status | READY or legacy unset status; CLOSED and ERROR fail closed |
OAuth authorization-code exchange, refresh-token storage, access-token refresh, tenant connection selection, revocation, and rotation belong to the platform integration-account lifecycle. Workflow inputs never handle OAuth secrets.
Configuration
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:xero-revenue-ops"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "EXPONENTIAL",
"initialDelayMs": 1000,
"maxDelayMs": 60000,
"jitter": false
},
"executionConfig": {"timeoutMs": 30000},
"payloadConfig": {
"parameters": "{\"operation\":\"list_invoices\",\"limit\":100}"
}
}
The integration-account value is symbolic. Persisted workflows use the generated relationship rather than a plaintext token or tenant identifier.
Operations
| Operation | Xero behavior | Side effect |
|---|---|---|
get_organisation | Reads the bound tenant's organisation profile. | Read-only; safe retries. |
list_contacts | Reads one bounded contact page or continues pages. | Read-only; safe retries. |
get_contact | Reads one contact by UUID. | Read-only; safe retries. |
create_contact | Creates one allowlisted contact; Name is required. | New accounting entity; single attempt. |
update_contact | Updates one allowlisted contact at an explicit UUID. | Accounting mutation; single attempt. |
list_invoices | Reads one bounded invoice page or continues pages. | Read-only; safe retries. |
get_invoice | Reads one invoice by UUID. | Read-only; safe retries. |
create_invoice | Creates one receivable or payable invoice with bounded line items. | New financial transaction; single attempt. |
update_invoice | Updates one invoice at an explicit UUID. | Financial mutation; single attempt. |
email_invoice | Requests Xero delivery for one invoice UUID. | External delivery; single attempt. |
list_payments | Reads one bounded payment page or continues pages. | Read-only; safe retries. |
get_payment | Reads one payment by UUID. | Read-only; safe retries. |
create_payment | Records a positive payment against explicit invoice and account UUIDs. | Financial mutation; single attempt. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Missing/invalid tenant, UUID, payload, reference, line item, or pagination bound. | No | Correct input; no unsafe request was sent. |
UNSUPPORTED_OPERATION | Unknown operation. | No | Select a documented operation. |
INTEGRATION_ACCOUNT_REQUIRED | No bound account. | No | Bind a Xero IntegrationAccount. |
INTEGRATION_ACCOUNT_NOT_READY | Account is closed/error. | No | Repair or reconnect the account. |
XERO_HTTP_400 | Xero rejected an entity field or accounting rule. | No | Inspect the safe validation detail and correct the request. |
XERO_HTTP_401 / 403 | Token invalid/expired, tenant disconnected, or scope missing. | No | Refresh/re-authorize the IntegrationAccount and verify tenant access. |
XERO_HTTP_404 | Entity is absent or invisible in the bound tenant. | No | Re-read/list with the same account and tenant. |
XERO_HTTP_409 | Concurrency or business-rule conflict. | No | Read current state and rebuild the mutation. |
XERO_HTTP_429 / 5xx | Rate limit or transient provider failure. | Yes for reads | Honor Retry-After; reconcile writes before repeating them. |
NETWORK_ERROR | Timeout, DNS, TLS, or connectivity failure. | Yes for reads | Verify connectivity and provider state. |
RESPONSE_TOO_LARGE | Response exceeded 5 MiB. | No | Lower the limit or split the read. |
INVALID_PROVIDER_RESPONSE | Expected entity array was absent or invalid. | No | Use requestId to verify provider/API compatibility. |
Example
Create one approved accounts-receivable invoice:
{
"operation": "create_invoice",
"invoice": {
"Type": "ACCREC",
"Contact": {
"ContactID": "11111111-2222-4333-8444-555555555555"
},
"Date": "2026-08-08",
"DueDate": "2026-09-07",
"InvoiceNumber": "VALKYR-4404",
"Reference": "Workflow milestone valkyr-4404",
"LineItems": [
{
"Description": "Workflow automation milestone",
"Quantity": 1,
"UnitAmount": 250,
"AccountCode": "200"
}
]
}
}
Expected result:
{
"status": "success",
"operation": "create_invoice",
"attempts": 1,
"httpStatus": 200,
"InvoiceID": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
"InvoiceNumber": "VALKYR-4404",
"data": {
"InvoiceID": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
"InvoiceNumber": "VALKYR-4404",
"Status": "DRAFT"
}
}
Notes
- Pagination: list operations send only bounded numeric
pageandpageSizeparameters to allowlisted endpoints. Provider-returned URLs and arbitrary caller filters are never followed or executed. Pages are capped at 100 items and total output at 10,000. - Rate limits: reads retry HTTP 408, 429, 500, 502, 503, and 504 according to
RetryPolicy, honoring numericRetry-After. Writes never retry automatically. Xero also applies per-minute, daily, and concurrency limits that can vary by application and tenant. - API limits: provider responses are capped at 5 MiB, structured payloads at 512 KiB, and invoice line arrays at 1,000 entries.
- Idempotency: creates, updates, payment recording, and invoice delivery are single-attempt. Use caller-owned
InvoiceNumber,Reference,ContactNumber, or workflow correlation fields and reconcile provider state after ambiguous failures. - Concurrency: Xero may reject changes when a record has changed or an accounting period is locked. The module never hides a preliminary read inside a financial mutation.
- Destructive behavior: this release exposes no delete, archive, void, refund, credit-note, bank-transaction, manual-journal, batch, or attachment operation. Writes can still affect accounting state and require approval appropriate to the workflow.
- API behavior: Xero can apply tenant taxes, currencies, tracking, branding, lock dates, account mappings, invoice numbering, rounding, and validation rules beyond local structural validation.
- Security: credentials remain in IntegrationAccount SecureFields. Fixed-host routing, UUID validation, allowlisted fields, bounded responses, and redacted provider details prevent arbitrary routing or token reflection.
- External verification: request construction, pagination, validation, retry safety, redaction, response mapping, and metadata discovery have deterministic local tests. Live OAuth scopes, tenant settings, taxation, invoice delivery, rate limits, and accounting effects require separately authorized provider credentials and are not exercised in repository tests.
- Deferred operations: bank transactions, credit notes, quotes, purchase orders, items, employees, reports, journals, accounts, taxes, tracking categories, attachments, invoice PDFs, overpayments, prepayments, refunds, batch requests, webhooks/triggers, OAuth refresh, arbitrary filters, and destructive operations.
- Functional references: n8n Xero node source, n8n Xero OAuth credential source, and Xero Accounting API.