Skip to main content

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_organisation
  • list_contacts, get_contact, create_contact, update_contact
  • list_invoices, get_invoice, create_invoice, update_invoice, email_invoice
  • list_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

  1. Create a Xero OAuth 2.0 application and authorize offline_access, accounting.contacts, accounting.transactions, and accounting.settings as required by the selected operations.
  2. Store the current OAuth access token in the encrypted apiKey field of an IntegrationAccount.
  3. Store the selected Xero connection tenant UUID in IntegrationAccount.accountId and set status to READY.
  4. Bind that account through ExecModuleConfig.authConfig.integrationAccount.
  5. Use get_organisation to verify the intended tenant before financial mutations.
  6. Use a stable InvoiceNumber, Reference, or workflow correlation value when creating records.
  7. 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

NameTypeRequirementDefaultDescription and constraints
operationstringRequiredNoneOne of the 13 documented operations.
resourceIdstringGet, update, and email operationsNoneCanonical Xero entity UUID.
contactobject or JSON stringContact create/updateNoneAllowlisted contact fields, 512 KiB maximum.
invoiceobject or JSON stringInvoice create/updateNoneAllowlisted invoice fields and 1–1,000 line items, 512 KiB maximum.
paymentobject or JSON stringPayment createNoneAllowlisted payment fields with invoice and account UUID references.
pageintegerOptional list continuation1One-based list page.
limitintegerOptional100Maximum emitted resources, 1–10,000.
returnAllbooleanOptionalfalseContinue 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

NameTypeWhen presentDescription
statusstringAlwayssuccess or error.
operationstringAlwaysNormalized operation name.
attemptsintegerAlwaysProvider attempts across all pages.
httpStatusintegerProvider respondedLast Xero HTTP status.
dataobjectSingle-resource/write successProvider organisation, contact, invoice, payment, or email confirmation.
ContactID, InvoiceID, PaymentID, OrganisationIDstringProvider returns fieldSafe convenience identifiers.
InvoiceNumberstringProvider returns fieldXero invoice number.
items / countarray / integerList successBounded entity results and emitted count.
hasMorebooleanList successWhether another numeric page may exist.
nextPageintegerAnother page may existOne-based continuation page.
requestIdstringXero returns oneSafe correlation or request trace reference.
errorobjectFailureSafe {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

SettingRequirement
ProviderXero OAuth 2.0 application
Scopesoffline_access, accounting.contacts, accounting.transactions, and accounting.settings as needed
accountIdTenant UUID returned by the Xero Connections API
apiKeyCurrent OAuth access token in an encrypted SecureField
passwordLegacy encrypted token fallback only; prefer apiKey
statusREADY 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

OperationXero behaviorSide effect
get_organisationReads the bound tenant's organisation profile.Read-only; safe retries.
list_contactsReads one bounded contact page or continues pages.Read-only; safe retries.
get_contactReads one contact by UUID.Read-only; safe retries.
create_contactCreates one allowlisted contact; Name is required.New accounting entity; single attempt.
update_contactUpdates one allowlisted contact at an explicit UUID.Accounting mutation; single attempt.
list_invoicesReads one bounded invoice page or continues pages.Read-only; safe retries.
get_invoiceReads one invoice by UUID.Read-only; safe retries.
create_invoiceCreates one receivable or payable invoice with bounded line items.New financial transaction; single attempt.
update_invoiceUpdates one invoice at an explicit UUID.Financial mutation; single attempt.
email_invoiceRequests Xero delivery for one invoice UUID.External delivery; single attempt.
list_paymentsReads one bounded payment page or continues pages.Read-only; safe retries.
get_paymentReads one payment by UUID.Read-only; safe retries.
create_paymentRecords a positive payment against explicit invoice and account UUIDs.Financial mutation; single attempt.

Errors and Failure Modes

CodeTypical causeRetryableResolution
VALIDATION_ERRORMissing/invalid tenant, UUID, payload, reference, line item, or pagination bound.NoCorrect input; no unsafe request was sent.
UNSUPPORTED_OPERATIONUnknown operation.NoSelect a documented operation.
INTEGRATION_ACCOUNT_REQUIREDNo bound account.NoBind a Xero IntegrationAccount.
INTEGRATION_ACCOUNT_NOT_READYAccount is closed/error.NoRepair or reconnect the account.
XERO_HTTP_400Xero rejected an entity field or accounting rule.NoInspect the safe validation detail and correct the request.
XERO_HTTP_401 / 403Token invalid/expired, tenant disconnected, or scope missing.NoRefresh/re-authorize the IntegrationAccount and verify tenant access.
XERO_HTTP_404Entity is absent or invisible in the bound tenant.NoRe-read/list with the same account and tenant.
XERO_HTTP_409Concurrency or business-rule conflict.NoRead current state and rebuild the mutation.
XERO_HTTP_429 / 5xxRate limit or transient provider failure.Yes for readsHonor Retry-After; reconcile writes before repeating them.
NETWORK_ERRORTimeout, DNS, TLS, or connectivity failure.Yes for readsVerify connectivity and provider state.
RESPONSE_TOO_LARGEResponse exceeded 5 MiB.NoLower the limit or split the read.
INVALID_PROVIDER_RESPONSEExpected entity array was absent or invalid.NoUse 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 page and pageSize parameters 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 numeric Retry-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.