# Xero Proxy documentation Generated from https://xero-proxy.fullstackplus.com.au. Route and scope tables are generated from the same table the proxy enforces. # Overview What the proxy does, what it deliberately does not do, and how a request travels through it. ## What this is Xero Proxy holds one OAuth connection to a Xero organisation and fans it out to as many downstream applications as you like, each with its own revocable API key. Xero sees a single connected app; you see one credential per integration, scoped and auditable. - A one-to-one passthrough: paths, query strings, headers and bodies are forwarded unchanged, and Xero's status codes, bodies and rate-limit headers come back verbatim. - Three independent restrictions on every key: which organisations it may reach, which scopes it holds, and which network addresses it may call from. - Deny by default: a method and path that is not in the route table never reaches Xero. - Token refresh is single-flight across every container, so fifty concurrent callers hitting an expired token produce exactly one refresh. ## The request lifecycle 1. Credential: the bearer token is parsed, the key looked up by its public ID and the secret verified against its hash. There is no cache, so a revocation applies to the very next request. 2. Network scope: if the key carries an allowlist, the calling address must match it. 3. Route mapping: the method and path are resolved against the route table. An unmapped route is denied and logged. 4. Tenant selection: a single-organisation key has its Xero-Tenant-Id filled in; a multi-organisation key must state one, and it must be in the key's own set. 5. Permission scope: the required scope must be in the intersection of what the key was granted and what the connection currently holds. 6. Replay and budget: an account may have read caching, idempotent writes or a quota policy turned on. A request answered from a stored read or a recorded write stops here and spends nothing; otherwise the policy decides whether this key may spend what is left. 7. Forward: the request is streamed to Xero with a valid access token, and the response streamed straight back. > Every step is a hard gate. A request that fails any of them never reaches Xero, and the refusal is logged with the reason. ## What it does not do - It does not transform payloads. There is no schema mapping layer to fall out of date, and a response is Xero's own bytes unless this page says otherwise. - It does not absorb Xero's rate limiting. Xero's 429s pass through with `Retry-After` intact, so your existing backoff keeps working exactly as it did against Xero directly. - It does not decide anything about your traffic on your behalf. Caching, quota governance, idempotent writes, body capture, deferred execution and completion callbacks are each off until an account turns them on, and an account that turns none of them on gets a pure passthrough. - It does not call anywhere you did not ask it to. The only addresses it will send to are ones an account owner registered in the dashboard, for Xero event fan-out, alert notifications and completion callbacks. A proxied request cannot name a destination, and every registered URL must be public https. - It does not hold data it was not asked to hold. Request and response bodies are stored only by the features you enable, always encrypted at rest, always with a stated expiry. # Getting started Swap the base URL and the credential. Nothing else in your integration moves. ## What changes ```diff - https://api.xero.com/api.xro/2.0/Invoices + https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices - Authorization: Bearer + Authorization: Bearer xpk_live__ ``` Query strings, request and response bodies, `Accept` (including XML and PDF), `If-Modified-Since`, pagination and filters all pass through untouched. `Idempotency-Key` is forwarded too, and an account can additionally have the proxy honour it: see [idempotent writes](/developers/idempotency). ## Your first request ```bash curl -s https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices \ -H "Authorization: Bearer xpk_live__" \ -H "Xero-Tenant-Id: " \ -H "Accept: application/json" ``` If the key is scoped to exactly one organisation you can drop the `Xero-Tenant-Id` header and it will be filled in for you. ## Discovering which organisations a key can reach ```bash curl -s https://xero-proxy.fullstackplus.com.au/connections \ -H "Authorization: Bearer xpk_live__" ``` The response is in Xero's own `/connections` shape, filtered to the tenants this key may reach, so client libraries that call it on startup work unmodified. ## Before you go live 1. Connect the Xero organisation once, from the dashboard. 2. Mint one key per downstream application, not one shared key. 3. Grant each key only the scopes that application actually calls. 4. Add a network allowlist to any key that calls from fixed addresses. 5. Set an expiry on keys issued to third parties, and rotate on a schedule. # Authentication and keys Key format, the Xero-Tenant-Id rule, rotation with a grace window, and instant revocation. ## Key format ```text xpk_live__ ``` - `` is public and is what we look up on. `` is 256 bits of machine entropy, stored only as a hash. - The full credential is shown exactly once, when the key is created or rotated. It cannot be retrieved afterwards. - `xpk_test_` keys are a separate environment: a test prefix never resolves a live key, even if the rest matches. ## The Xero-Tenant-Id rule 1. Key scoped to one organisation, header absent: we fill it in for you. 2. Key scoped to several organisations, header absent: `400 tenant_required`, listing the tenant IDs you may use. 3. Header present but not in the key's set: `403 tenant_not_allowed`, whether that organisation belongs to another account, was guessed, or does not exist. ## Rotation and revocation - Rotation mints a replacement carrying the same organisations, scopes and allowlist, and gives the old credential a grace window (24 hours by default) during which both work. - Revocation is immediate: keys are read from the database on every request, so there is no cache window to wait out. - An expiry date on a key is enforced on the request path, not by a sweep, so a key stops working the moment it passes. ## Management-plane scopes A key can additionally carry management scopes, which grant access to this platform's own control plane through the management MCP server. They are held separately from Xero scopes and have no effect on proxied traffic. | Scope | What it grants | | --- | --- | | management.connections.read | Read Xero connection health and the organisations behind it | | management.keys.read | List API keys and their scoping (never their secrets) | | management.keys.write | Create, rotate and revoke API keys | | management.usage.read | Read request volume, error rates and the recent request log | | management.diagnostics.read | Inspect one request by its id, including any bodies captured during a debug window | | management.diagnostics.write | Start and stop time-boxed body capture on a key | | management.alerts.read | Read open and acknowledged alerts | | management.alerts.write | Acknowledge and resolve alerts | | management.cache.read | Read the caching settings and what caching is saving | | management.cache.write | Purge cached responses | | management.limits.read | Read the quota policy, per-key limits and the organisation's remaining Xero budget | | management.limits.write | Change per-key rate limits, daily quotas and priority | | management.idempotency.read | Read the write-safety settings and which writes are currently protected against replay | | management.idempotency.write | Forget stored write responses, so the next retry of each reaches Xero | | management.deferred.read | Read the deferred-execution settings and the queue of work accepted for later | | management.webhooks.read | Read event subscriptions and their delivery history | | management.webhooks.write | Pause, resume and re-queue event deliveries | | management.callbacks.read | Read completion-callback endpoints and whether they are being delivered to | | management.callbacks.write | Re-queue a completion callback that exhausted its retries | # Scopes Xero's own scope strings, used unchanged, and how a key's grant is narrowed by the connection. ## How scoping works A key's effective permission on an organisation is the intersection of the scopes granted to the key and the scopes the underlying Xero connection currently holds. ```text effective = key.scopes ∩ connection.grantedScopes ``` Re-authorising a connection with fewer scopes therefore narrows every key on it rather than breaking them with a Xero-side error. Read-only variants are distinct grants: `accounting.transactions.read` never permits a write. ## Scope reference **Accounting** | Scope | Access | What it grants | | --- | --- | --- | | accounting.transactions | read and write | Create and update invoices, bills, credit notes and payments | | accounting.transactions.read | read only | Read invoices, bills, credit notes and payments | | accounting.contacts | read and write | Create and update contacts and contact groups | | accounting.contacts.read | read only | Read contacts and contact groups | | accounting.settings | read and write | Create and update accounts, tax rates, tracking categories and organisation settings | | accounting.settings.read | read only | Read chart of accounts, tax rates and organisation settings | | accounting.reports.read | read only | Read accounting reports such as Balance Sheet and Profit & Loss | | accounting.reports.tenninetynine.read | read only | Read the 1099 report | | accounting.journals.read | read only | Read the general ledger journals feed | | accounting.attachments | read and write | Upload and replace attachments on accounting records | | accounting.attachments.read | read only | Download attachments on accounting records | | accounting.budgets.read | read only | Read budgets | **Payroll AU** | Scope | Access | What it grants | | --- | --- | --- | | payroll.employees | read and write | Create and update Australian payroll employees | | payroll.employees.read | read only | Read Australian payroll employees | | payroll.payruns | read and write | Create and update Australian pay runs and pay slips | | payroll.payruns.read | read only | Read Australian pay runs and pay slips | | payroll.payslip | read and write | Update Australian pay slips | | payroll.payslip.read | read only | Read Australian pay slips | | payroll.timesheets | read and write | Create and update Australian timesheets | | payroll.timesheets.read | read only | Read Australian timesheets | | payroll.settings | read and write | Update Australian payroll settings and calendars | | payroll.settings.read | read only | Read Australian payroll settings and calendars | | payroll.superfunds | read and write | Create and update superannuation funds | | payroll.superfunds.read | read only | Read superannuation funds | | payroll.superfundproducts.read | read only | Read the superannuation fund product register | **Payroll UK** | Scope | Access | What it grants | | --- | --- | --- | | payroll_uk.employees | read and write | Create and update UK payroll employees | | payroll_uk.employees.read | read only | Read UK payroll employees | | payroll_uk.payruns | read and write | Create and update UK pay runs | | payroll_uk.payruns.read | read only | Read UK pay runs | | payroll_uk.payslip | read and write | Update UK pay slips | | payroll_uk.payslip.read | read only | Read UK pay slips | | payroll_uk.timesheets | read and write | Create and update UK timesheets | | payroll_uk.timesheets.read | read only | Read UK timesheets | | payroll_uk.settings | read and write | Update UK payroll settings | | payroll_uk.settings.read | read only | Read UK payroll settings | **Payroll NZ** | Scope | Access | What it grants | | --- | --- | --- | | payroll_nz.employees | read and write | Create and update New Zealand payroll employees | | payroll_nz.employees.read | read only | Read New Zealand payroll employees | | payroll_nz.payruns | read and write | Create and update New Zealand pay runs | | payroll_nz.payruns.read | read only | Read New Zealand pay runs | | payroll_nz.payslip | read and write | Update New Zealand pay slips | | payroll_nz.payslip.read | read only | Read New Zealand pay slips | | payroll_nz.timesheets | read and write | Create and update New Zealand timesheets | | payroll_nz.timesheets.read | read only | Read New Zealand timesheets | | payroll_nz.settings | read and write | Update New Zealand payroll settings | | payroll_nz.settings.read | read only | Read New Zealand payroll settings | **Files** | Scope | Access | What it grants | | --- | --- | --- | | files | read and write | Upload, update and delete files and folders | | files.read | read only | Read and download files and folders | **Assets** | Scope | Access | What it grants | | --- | --- | --- | | assets | read and write | Create and update fixed assets | | assets.read | read only | Read fixed assets and asset types | **Projects** | Scope | Access | What it grants | | --- | --- | --- | | projects | read and write | Create and update projects, tasks and time entries | | projects.read | read only | Read projects, tasks and time entries | **Bank Feeds** | Scope | Access | What it grants | | --- | --- | --- | | bankfeeds | read and write | Create bank feed connections and deliver statements | **Finance** | Scope | Access | What it grants | | --- | --- | --- | | finance.statements.read | read only | Read bank statements accounting data | | finance.accountingactivity.read | read only | Read accounting activity metrics | | finance.cashvalidation.read | read only | Read cash validation data | | finance.bankstatementsplus.read | read only | Read enriched bank statement data | # Route reference Every mapped endpoint and the scope it requires, generated from the enforcement table. ## Supported API families | Family | Base path | | --- | --- | | Accounting | /api.xro/2.0 | | Payroll AU (1.0) | /payroll.xro/1.0 | | Payroll AU | /payroll.xro/2.0 | | Payroll UK | /payroll.xro/2.0/uk | | Payroll NZ | /payroll.xro/2.0/nz | | Files | /files.xro/1.0 | | Assets | /assets.xro/1.0 | | Projects | /projects.xro/2.0 | | Bank Feeds | /bankfeeds.xro/1.0 | | Finance | /finance.xro/1.0 | ## Endpoints 573 mapped endpoints across 10 API families. Every required scope below is read from the same table the enforcement middleware checks, so this reference cannot drift from what is actually enforced. Anything not listed is denied before it reaches Xero. **Accounting** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /api.xro/2.0/Invoices | accounting.transactions.read | List invoices | | POST, PUT | /api.xro/2.0/Invoices | accounting.transactions | Create or update invoices | | GET, HEAD | /api.xro/2.0/Invoices/{id} | accounting.transactions.read | Read a single invoices record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Invoices/{id} | accounting.transactions | Update or delete a single invoices record | | GET, HEAD | /api.xro/2.0/Invoices/{id}/Attachments | accounting.attachments.read | List attachments on a invoices record | | GET, HEAD | /api.xro/2.0/Invoices/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a invoices record | | POST, PUT | /api.xro/2.0/Invoices/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a invoices record | | GET, HEAD | /api.xro/2.0/Invoices/{id}/History | accounting.transactions.read | Read history and notes on a invoices record | | PUT, POST | /api.xro/2.0/Invoices/{id}/History | accounting.transactions | Add a history note to a invoices record | | GET, HEAD | /api.xro/2.0/BankTransactions | accounting.transactions.read | List bank transactions | | POST, PUT | /api.xro/2.0/BankTransactions | accounting.transactions | Create or update bank transactions | | GET, HEAD | /api.xro/2.0/BankTransactions/{id} | accounting.transactions.read | Read a single bank transactions record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/BankTransactions/{id} | accounting.transactions | Update or delete a single bank transactions record | | GET, HEAD | /api.xro/2.0/BankTransactions/{id}/Attachments | accounting.attachments.read | List attachments on a bank transactions record | | GET, HEAD | /api.xro/2.0/BankTransactions/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a bank transactions record | | POST, PUT | /api.xro/2.0/BankTransactions/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a bank transactions record | | GET, HEAD | /api.xro/2.0/BankTransactions/{id}/History | accounting.transactions.read | Read history and notes on a bank transactions record | | PUT, POST | /api.xro/2.0/BankTransactions/{id}/History | accounting.transactions | Add a history note to a bank transactions record | | GET, HEAD | /api.xro/2.0/BankTransfers | accounting.transactions.read | List bank transfers | | POST, PUT | /api.xro/2.0/BankTransfers | accounting.transactions | Create or update bank transfers | | GET, HEAD | /api.xro/2.0/BankTransfers/{id} | accounting.transactions.read | Read a single bank transfers record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/BankTransfers/{id} | accounting.transactions | Update or delete a single bank transfers record | | GET, HEAD | /api.xro/2.0/BankTransfers/{id}/Attachments | accounting.attachments.read | List attachments on a bank transfers record | | GET, HEAD | /api.xro/2.0/BankTransfers/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a bank transfers record | | POST, PUT | /api.xro/2.0/BankTransfers/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a bank transfers record | | GET, HEAD | /api.xro/2.0/BankTransfers/{id}/History | accounting.transactions.read | Read history and notes on a bank transfers record | | PUT, POST | /api.xro/2.0/BankTransfers/{id}/History | accounting.transactions | Add a history note to a bank transfers record | | GET, HEAD | /api.xro/2.0/BatchPayments | accounting.transactions.read | List batch payments | | POST, PUT | /api.xro/2.0/BatchPayments | accounting.transactions | Create or update batch payments | | GET, HEAD | /api.xro/2.0/BatchPayments/{id} | accounting.transactions.read | Read a single batch payments record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/BatchPayments/{id} | accounting.transactions | Update or delete a single batch payments record | | GET, HEAD | /api.xro/2.0/BatchPayments/{id}/Attachments | accounting.attachments.read | List attachments on a batch payments record | | GET, HEAD | /api.xro/2.0/BatchPayments/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a batch payments record | | POST, PUT | /api.xro/2.0/BatchPayments/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a batch payments record | | GET, HEAD | /api.xro/2.0/BatchPayments/{id}/History | accounting.transactions.read | Read history and notes on a batch payments record | | PUT, POST | /api.xro/2.0/BatchPayments/{id}/History | accounting.transactions | Add a history note to a batch payments record | | GET, HEAD | /api.xro/2.0/CreditNotes | accounting.transactions.read | List credit notes | | POST, PUT | /api.xro/2.0/CreditNotes | accounting.transactions | Create or update credit notes | | GET, HEAD | /api.xro/2.0/CreditNotes/{id} | accounting.transactions.read | Read a single credit notes record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/CreditNotes/{id} | accounting.transactions | Update or delete a single credit notes record | | GET, HEAD | /api.xro/2.0/CreditNotes/{id}/Attachments | accounting.attachments.read | List attachments on a credit notes record | | GET, HEAD | /api.xro/2.0/CreditNotes/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a credit notes record | | POST, PUT | /api.xro/2.0/CreditNotes/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a credit notes record | | GET, HEAD | /api.xro/2.0/CreditNotes/{id}/History | accounting.transactions.read | Read history and notes on a credit notes record | | PUT, POST | /api.xro/2.0/CreditNotes/{id}/History | accounting.transactions | Add a history note to a credit notes record | | GET, HEAD | /api.xro/2.0/Payments | accounting.transactions.read | List payments | | POST, PUT | /api.xro/2.0/Payments | accounting.transactions | Create or update payments | | GET, HEAD | /api.xro/2.0/Payments/{id} | accounting.transactions.read | Read a single payments record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Payments/{id} | accounting.transactions | Update or delete a single payments record | | GET, HEAD | /api.xro/2.0/Payments/{id}/Attachments | accounting.attachments.read | List attachments on a payments record | | GET, HEAD | /api.xro/2.0/Payments/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a payments record | | POST, PUT | /api.xro/2.0/Payments/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a payments record | | GET, HEAD | /api.xro/2.0/Payments/{id}/History | accounting.transactions.read | Read history and notes on a payments record | | PUT, POST | /api.xro/2.0/Payments/{id}/History | accounting.transactions | Add a history note to a payments record | | GET, HEAD | /api.xro/2.0/Prepayments | accounting.transactions.read | List prepayments | | POST, PUT | /api.xro/2.0/Prepayments | accounting.transactions | Create or update prepayments | | GET, HEAD | /api.xro/2.0/Prepayments/{id} | accounting.transactions.read | Read a single prepayments record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Prepayments/{id} | accounting.transactions | Update or delete a single prepayments record | | GET, HEAD | /api.xro/2.0/Prepayments/{id}/Attachments | accounting.attachments.read | List attachments on a prepayments record | | GET, HEAD | /api.xro/2.0/Prepayments/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a prepayments record | | POST, PUT | /api.xro/2.0/Prepayments/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a prepayments record | | GET, HEAD | /api.xro/2.0/Prepayments/{id}/History | accounting.transactions.read | Read history and notes on a prepayments record | | PUT, POST | /api.xro/2.0/Prepayments/{id}/History | accounting.transactions | Add a history note to a prepayments record | | GET, HEAD | /api.xro/2.0/Overpayments | accounting.transactions.read | List overpayments | | POST, PUT | /api.xro/2.0/Overpayments | accounting.transactions | Create or update overpayments | | GET, HEAD | /api.xro/2.0/Overpayments/{id} | accounting.transactions.read | Read a single overpayments record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Overpayments/{id} | accounting.transactions | Update or delete a single overpayments record | | GET, HEAD | /api.xro/2.0/Overpayments/{id}/Attachments | accounting.attachments.read | List attachments on a overpayments record | | GET, HEAD | /api.xro/2.0/Overpayments/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a overpayments record | | POST, PUT | /api.xro/2.0/Overpayments/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a overpayments record | | GET, HEAD | /api.xro/2.0/Overpayments/{id}/History | accounting.transactions.read | Read history and notes on a overpayments record | | PUT, POST | /api.xro/2.0/Overpayments/{id}/History | accounting.transactions | Add a history note to a overpayments record | | GET, HEAD | /api.xro/2.0/PurchaseOrders | accounting.transactions.read | List purchase orders | | POST, PUT | /api.xro/2.0/PurchaseOrders | accounting.transactions | Create or update purchase orders | | GET, HEAD | /api.xro/2.0/PurchaseOrders/{id} | accounting.transactions.read | Read a single purchase orders record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/PurchaseOrders/{id} | accounting.transactions | Update or delete a single purchase orders record | | GET, HEAD | /api.xro/2.0/PurchaseOrders/{id}/Attachments | accounting.attachments.read | List attachments on a purchase orders record | | GET, HEAD | /api.xro/2.0/PurchaseOrders/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a purchase orders record | | POST, PUT | /api.xro/2.0/PurchaseOrders/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a purchase orders record | | GET, HEAD | /api.xro/2.0/PurchaseOrders/{id}/History | accounting.transactions.read | Read history and notes on a purchase orders record | | PUT, POST | /api.xro/2.0/PurchaseOrders/{id}/History | accounting.transactions | Add a history note to a purchase orders record | | GET, HEAD | /api.xro/2.0/Quotes | accounting.transactions.read | List quotes | | POST, PUT | /api.xro/2.0/Quotes | accounting.transactions | Create or update quotes | | GET, HEAD | /api.xro/2.0/Quotes/{id} | accounting.transactions.read | Read a single quotes record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Quotes/{id} | accounting.transactions | Update or delete a single quotes record | | GET, HEAD | /api.xro/2.0/Quotes/{id}/Attachments | accounting.attachments.read | List attachments on a quotes record | | GET, HEAD | /api.xro/2.0/Quotes/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a quotes record | | POST, PUT | /api.xro/2.0/Quotes/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a quotes record | | GET, HEAD | /api.xro/2.0/Quotes/{id}/History | accounting.transactions.read | Read history and notes on a quotes record | | PUT, POST | /api.xro/2.0/Quotes/{id}/History | accounting.transactions | Add a history note to a quotes record | | GET, HEAD | /api.xro/2.0/Receipts | accounting.transactions.read | List receipts | | POST, PUT | /api.xro/2.0/Receipts | accounting.transactions | Create or update receipts | | GET, HEAD | /api.xro/2.0/Receipts/{id} | accounting.transactions.read | Read a single receipts record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Receipts/{id} | accounting.transactions | Update or delete a single receipts record | | GET, HEAD | /api.xro/2.0/Receipts/{id}/Attachments | accounting.attachments.read | List attachments on a receipts record | | GET, HEAD | /api.xro/2.0/Receipts/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a receipts record | | POST, PUT | /api.xro/2.0/Receipts/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a receipts record | | GET, HEAD | /api.xro/2.0/Receipts/{id}/History | accounting.transactions.read | Read history and notes on a receipts record | | PUT, POST | /api.xro/2.0/Receipts/{id}/History | accounting.transactions | Add a history note to a receipts record | | GET, HEAD | /api.xro/2.0/ExpenseClaims | accounting.transactions.read | List expense claims | | POST, PUT | /api.xro/2.0/ExpenseClaims | accounting.transactions | Create or update expense claims | | GET, HEAD | /api.xro/2.0/ExpenseClaims/{id} | accounting.transactions.read | Read a single expense claims record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/ExpenseClaims/{id} | accounting.transactions | Update or delete a single expense claims record | | GET, HEAD | /api.xro/2.0/ExpenseClaims/{id}/Attachments | accounting.attachments.read | List attachments on a expense claims record | | GET, HEAD | /api.xro/2.0/ExpenseClaims/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a expense claims record | | POST, PUT | /api.xro/2.0/ExpenseClaims/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a expense claims record | | GET, HEAD | /api.xro/2.0/ExpenseClaims/{id}/History | accounting.transactions.read | Read history and notes on a expense claims record | | PUT, POST | /api.xro/2.0/ExpenseClaims/{id}/History | accounting.transactions | Add a history note to a expense claims record | | GET, HEAD | /api.xro/2.0/ManualJournals | accounting.transactions.read | List manual journals | | POST, PUT | /api.xro/2.0/ManualJournals | accounting.transactions | Create or update manual journals | | GET, HEAD | /api.xro/2.0/ManualJournals/{id} | accounting.transactions.read | Read a single manual journals record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/ManualJournals/{id} | accounting.transactions | Update or delete a single manual journals record | | GET, HEAD | /api.xro/2.0/ManualJournals/{id}/Attachments | accounting.attachments.read | List attachments on a manual journals record | | GET, HEAD | /api.xro/2.0/ManualJournals/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a manual journals record | | POST, PUT | /api.xro/2.0/ManualJournals/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a manual journals record | | GET, HEAD | /api.xro/2.0/ManualJournals/{id}/History | accounting.transactions.read | Read history and notes on a manual journals record | | PUT, POST | /api.xro/2.0/ManualJournals/{id}/History | accounting.transactions | Add a history note to a manual journals record | | GET, HEAD | /api.xro/2.0/LinkedTransactions | accounting.transactions.read | List linked transactions | | POST, PUT | /api.xro/2.0/LinkedTransactions | accounting.transactions | Create or update linked transactions | | GET, HEAD | /api.xro/2.0/LinkedTransactions/{id} | accounting.transactions.read | Read a single linked transactions record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/LinkedTransactions/{id} | accounting.transactions | Update or delete a single linked transactions record | | GET, HEAD | /api.xro/2.0/LinkedTransactions/{id}/Attachments | accounting.attachments.read | List attachments on a linked transactions record | | GET, HEAD | /api.xro/2.0/LinkedTransactions/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a linked transactions record | | POST, PUT | /api.xro/2.0/LinkedTransactions/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a linked transactions record | | GET, HEAD | /api.xro/2.0/LinkedTransactions/{id}/History | accounting.transactions.read | Read history and notes on a linked transactions record | | PUT, POST | /api.xro/2.0/LinkedTransactions/{id}/History | accounting.transactions | Add a history note to a linked transactions record | | GET, HEAD | /api.xro/2.0/PaymentServices | accounting.transactions.read | List payment services | | POST, PUT | /api.xro/2.0/PaymentServices | accounting.transactions | Create or update payment services | | GET, HEAD | /api.xro/2.0/PaymentServices/{id} | accounting.transactions.read | Read a single payment services record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/PaymentServices/{id} | accounting.transactions | Update or delete a single payment services record | | GET, HEAD | /api.xro/2.0/PaymentServices/{id}/Attachments | accounting.attachments.read | List attachments on a payment services record | | GET, HEAD | /api.xro/2.0/PaymentServices/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a payment services record | | POST, PUT | /api.xro/2.0/PaymentServices/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a payment services record | | GET, HEAD | /api.xro/2.0/PaymentServices/{id}/History | accounting.transactions.read | Read history and notes on a payment services record | | PUT, POST | /api.xro/2.0/PaymentServices/{id}/History | accounting.transactions | Add a history note to a payment services record | | GET, HEAD | /api.xro/2.0/CreditNotes/{id}/Allocations | accounting.transactions.read | List the allocations on a credit note | | PUT, POST | /api.xro/2.0/CreditNotes/{id}/Allocations | accounting.transactions | Allocate a credit note to an invoice or bill | | DELETE | /api.xro/2.0/CreditNotes/{id}/Allocations/{allocationId} | accounting.transactions | Remove one allocation from a credit note | | GET, HEAD | /api.xro/2.0/Prepayments/{id}/Allocations | accounting.transactions.read | List the allocations on a prepayment | | PUT, POST | /api.xro/2.0/Prepayments/{id}/Allocations | accounting.transactions | Allocate a prepayment to an invoice or bill | | DELETE | /api.xro/2.0/Prepayments/{id}/Allocations/{allocationId} | accounting.transactions | Remove one allocation from a prepayment | | GET, HEAD | /api.xro/2.0/Overpayments/{id}/Allocations | accounting.transactions.read | List the allocations on a overpayment | | PUT, POST | /api.xro/2.0/Overpayments/{id}/Allocations | accounting.transactions | Allocate a overpayment to an invoice or bill | | DELETE | /api.xro/2.0/Overpayments/{id}/Allocations/{allocationId} | accounting.transactions | Remove one allocation from a overpayment | | GET, HEAD | /api.xro/2.0/Invoices/{id}/OnlineInvoice | accounting.transactions.read | Read the online invoice URL for an invoice | | POST | /api.xro/2.0/Invoices/{id}/Email | accounting.transactions | Send an invoice by email from Xero | | POST, DELETE | /api.xro/2.0/BatchPayments/{id} | accounting.transactions | Delete or reverse a batch payment | | GET, HEAD | /api.xro/2.0/Contacts | accounting.contacts.read | List contacts | | POST, PUT | /api.xro/2.0/Contacts | accounting.contacts | Create or update contacts | | GET, HEAD | /api.xro/2.0/Contacts/{id} | accounting.contacts.read | Read a single contacts record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Contacts/{id} | accounting.contacts | Update or delete a single contacts record | | GET, HEAD | /api.xro/2.0/Contacts/{id}/Attachments | accounting.attachments.read | List attachments on a contact record | | GET, HEAD | /api.xro/2.0/Contacts/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a contact record | | POST, PUT | /api.xro/2.0/Contacts/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a contact record | | GET, HEAD | /api.xro/2.0/Contacts/{id}/History | accounting.contacts.read | Read history and notes on a contact record | | PUT, POST | /api.xro/2.0/Contacts/{id}/History | accounting.contacts | Add a history note to a contact record | | GET, HEAD | /api.xro/2.0/ContactGroups | accounting.contacts.read | List contact groups | | POST, PUT | /api.xro/2.0/ContactGroups | accounting.contacts | Create or update contact groups | | GET, HEAD | /api.xro/2.0/ContactGroups/{id} | accounting.contacts.read | Read a single contact groups record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/ContactGroups/{id} | accounting.contacts | Update or delete a single contact groups record | | GET, HEAD | /api.xro/2.0/Contacts/{id}/CISSettings | accounting.contacts.read | Read CIS settings for a contact | | GET, HEAD | /api.xro/2.0/ContactGroups/{id}/Contacts | accounting.contacts.read | List the contacts in a contact group | | PUT, POST, DELETE | /api.xro/2.0/ContactGroups/{id}/Contacts | accounting.contacts | Add or clear the contacts in a contact group | | DELETE | /api.xro/2.0/ContactGroups/{id}/Contacts/{contactId} | accounting.contacts | Remove one contact from a contact group | | GET, HEAD | /api.xro/2.0/Accounts | accounting.settings.read | List chart of accounts | | POST, PUT | /api.xro/2.0/Accounts | accounting.settings | Create or update chart of accounts | | GET, HEAD | /api.xro/2.0/Accounts/{id} | accounting.settings.read | Read a single chart of accounts record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Accounts/{id} | accounting.settings | Update or delete a single chart of accounts record | | GET, HEAD | /api.xro/2.0/BrandingThemes | accounting.settings.read | List branding themes | | POST, PUT | /api.xro/2.0/BrandingThemes | accounting.settings | Create or update branding themes | | GET, HEAD | /api.xro/2.0/BrandingThemes/{id} | accounting.settings.read | Read a single branding themes record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/BrandingThemes/{id} | accounting.settings | Update or delete a single branding themes record | | GET, HEAD | /api.xro/2.0/Currencies | accounting.settings.read | List currencies | | POST, PUT | /api.xro/2.0/Currencies | accounting.settings | Create or update currencies | | GET, HEAD | /api.xro/2.0/Currencies/{id} | accounting.settings.read | Read a single currencies record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Currencies/{id} | accounting.settings | Update or delete a single currencies record | | GET, HEAD | /api.xro/2.0/Employees | accounting.settings.read | List employees (accounting) | | POST, PUT | /api.xro/2.0/Employees | accounting.settings | Create or update employees (accounting) | | GET, HEAD | /api.xro/2.0/Employees/{id} | accounting.settings.read | Read a single employees (accounting) record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Employees/{id} | accounting.settings | Update or delete a single employees (accounting) record | | GET, HEAD | /api.xro/2.0/Items | accounting.settings.read | List items | | POST, PUT | /api.xro/2.0/Items | accounting.settings | Create or update items | | GET, HEAD | /api.xro/2.0/Items/{id} | accounting.settings.read | Read a single items record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Items/{id} | accounting.settings | Update or delete a single items record | | GET, HEAD | /api.xro/2.0/Organisation | accounting.settings.read | List organisation | | POST, PUT | /api.xro/2.0/Organisation | accounting.settings | Create or update organisation | | GET, HEAD | /api.xro/2.0/Organisation/{id} | accounting.settings.read | Read a single organisation record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Organisation/{id} | accounting.settings | Update or delete a single organisation record | | GET, HEAD | /api.xro/2.0/RepeatingInvoices | accounting.settings.read | List repeating invoices | | POST, PUT | /api.xro/2.0/RepeatingInvoices | accounting.settings | Create or update repeating invoices | | GET, HEAD | /api.xro/2.0/RepeatingInvoices/{id} | accounting.settings.read | Read a single repeating invoices record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/RepeatingInvoices/{id} | accounting.settings | Update or delete a single repeating invoices record | | GET, HEAD | /api.xro/2.0/TaxRates | accounting.settings.read | List tax rates | | POST, PUT | /api.xro/2.0/TaxRates | accounting.settings | Create or update tax rates | | GET, HEAD | /api.xro/2.0/TaxRates/{id} | accounting.settings.read | Read a single tax rates record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/TaxRates/{id} | accounting.settings | Update or delete a single tax rates record | | GET, HEAD | /api.xro/2.0/TrackingCategories | accounting.settings.read | List tracking categories | | POST, PUT | /api.xro/2.0/TrackingCategories | accounting.settings | Create or update tracking categories | | GET, HEAD | /api.xro/2.0/TrackingCategories/{id} | accounting.settings.read | Read a single tracking categories record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/TrackingCategories/{id} | accounting.settings | Update or delete a single tracking categories record | | GET, HEAD | /api.xro/2.0/Users | accounting.settings.read | List users | | POST, PUT | /api.xro/2.0/Users | accounting.settings | Create or update users | | GET, HEAD | /api.xro/2.0/Users/{id} | accounting.settings.read | Read a single users record | | POST, PUT, PATCH, DELETE | /api.xro/2.0/Users/{id} | accounting.settings | Update or delete a single users record | | GET, HEAD | /api.xro/2.0/Accounts/{id}/Attachments | accounting.attachments.read | List attachments on a account record | | GET, HEAD | /api.xro/2.0/Accounts/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a account record | | POST, PUT | /api.xro/2.0/Accounts/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a account record | | GET, HEAD | /api.xro/2.0/Accounts/{id}/History | accounting.settings.read | Read history and notes on a account record | | PUT, POST | /api.xro/2.0/Accounts/{id}/History | accounting.settings | Add a history note to a account record | | GET, HEAD | /api.xro/2.0/RepeatingInvoices/{id}/Attachments | accounting.attachments.read | List attachments on a repeating invoice record | | GET, HEAD | /api.xro/2.0/RepeatingInvoices/{id}/Attachments/* | accounting.attachments.read | Download an attachment from a repeating invoice record | | POST, PUT | /api.xro/2.0/RepeatingInvoices/{id}/Attachments/* | accounting.attachments | Upload or replace an attachment on a repeating invoice record | | GET, HEAD | /api.xro/2.0/RepeatingInvoices/{id}/History | accounting.settings.read | Read history and notes on a repeating invoice record | | PUT, POST | /api.xro/2.0/RepeatingInvoices/{id}/History | accounting.settings | Add a history note to a repeating invoice record | | POST, PUT | /api.xro/2.0/Setup | accounting.settings | Post conversion balances and historical data during setup | | GET, HEAD | /api.xro/2.0/Organisation/Actions | accounting.settings.read | Read which actions the organisation permits | | GET, HEAD | /api.xro/2.0/Organisation/{id}/CISSettings | accounting.settings.read | Read organisation CIS settings | | GET, HEAD | /api.xro/2.0/BrandingThemes/{id}/PaymentServices | accounting.settings.read | Read the payment services applied to a branding theme | | POST | /api.xro/2.0/BrandingThemes/{id}/PaymentServices | accounting.settings | Apply a payment service to a branding theme | | GET, HEAD | /api.xro/2.0/TrackingCategories/{id}/Options | accounting.settings.read | List the options on a tracking category | | POST, PUT | /api.xro/2.0/TrackingCategories/{id}/Options | accounting.settings | Create or update options on a tracking category | | POST, PUT, DELETE | /api.xro/2.0/TrackingCategories/{id}/Options/{optionId} | accounting.settings | Update or delete a single tracking category option | | GET, HEAD | /api.xro/2.0/InvoiceReminders/Settings | accounting.settings.read | Read invoice reminder settings | | GET, HEAD | /api.xro/2.0/Reports/* | accounting.reports.read | Run an accounting report (Balance Sheet, Profit & Loss, Aged reports) | | GET, HEAD | /api.xro/2.0/Reports | accounting.reports.read | List available accounting reports | | GET, HEAD | /api.xro/2.0/Reports/TenNinetyNine | accounting.reports.tenninetynine.read | Run the 1099 report | | GET, HEAD | /api.xro/2.0/Journals | accounting.journals.read | Read the general ledger journals feed | | GET, HEAD | /api.xro/2.0/Journals/{id} | accounting.journals.read | Read a single general ledger journal | | GET, HEAD | /api.xro/2.0/Budgets | accounting.budgets.read | List budgets | | GET, HEAD | /api.xro/2.0/Budgets/{id} | accounting.budgets.read | Read a single budget | **Payroll AU (1.0)** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /payroll.xro/1.0/Employees | payroll.employees.read | List employees | | POST, PUT | /payroll.xro/1.0/Employees | payroll.employees | Create or update employees | | GET, HEAD | /payroll.xro/1.0/Employees/{id} | payroll.employees.read | Read a single employees record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/Employees/{id} | payroll.employees | Update or delete a single employees record | | GET, HEAD | /payroll.xro/1.0/PayRuns | payroll.payruns.read | List pay runs | | POST, PUT | /payroll.xro/1.0/PayRuns | payroll.payruns | Create or update pay runs | | GET, HEAD | /payroll.xro/1.0/PayRuns/{id} | payroll.payruns.read | Read a single pay runs record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/PayRuns/{id} | payroll.payruns | Update or delete a single pay runs record | | GET, HEAD | /payroll.xro/1.0/Payslip | payroll.payslip.read | List pay slips | | POST, PUT | /payroll.xro/1.0/Payslip | payroll.payslip | Create or update pay slips | | GET, HEAD | /payroll.xro/1.0/Payslip/{id} | payroll.payslip.read | Read a single pay slips record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/Payslip/{id} | payroll.payslip | Update or delete a single pay slips record | | GET, HEAD | /payroll.xro/1.0/Timesheets | payroll.timesheets.read | List timesheets | | POST, PUT | /payroll.xro/1.0/Timesheets | payroll.timesheets | Create or update timesheets | | GET, HEAD | /payroll.xro/1.0/Timesheets/{id} | payroll.timesheets.read | Read a single timesheets record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/Timesheets/{id} | payroll.timesheets | Update or delete a single timesheets record | | GET, HEAD | /payroll.xro/1.0/PayItems | payroll.settings.read | List pay items | | POST, PUT | /payroll.xro/1.0/PayItems | payroll.settings | Create or update pay items | | GET, HEAD | /payroll.xro/1.0/PayItems/{id} | payroll.settings.read | Read a single pay items record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/PayItems/{id} | payroll.settings | Update or delete a single pay items record | | GET, HEAD | /payroll.xro/1.0/PayrollCalendars | payroll.settings.read | List payroll calendars | | POST, PUT | /payroll.xro/1.0/PayrollCalendars | payroll.settings | Create or update payroll calendars | | GET, HEAD | /payroll.xro/1.0/PayrollCalendars/{id} | payroll.settings.read | Read a single payroll calendars record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/PayrollCalendars/{id} | payroll.settings | Update or delete a single payroll calendars record | | GET, HEAD | /payroll.xro/1.0/Settings | payroll.settings.read | List payroll settings | | POST, PUT | /payroll.xro/1.0/Settings | payroll.settings | Create or update payroll settings | | GET, HEAD | /payroll.xro/1.0/Settings/{id} | payroll.settings.read | Read a single payroll settings record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/Settings/{id} | payroll.settings | Update or delete a single payroll settings record | | GET, HEAD | /payroll.xro/1.0/Superfunds | payroll.superfunds.read | List superannuation funds | | POST, PUT | /payroll.xro/1.0/Superfunds | payroll.superfunds | Create or update superannuation funds | | GET, HEAD | /payroll.xro/1.0/Superfunds/{id} | payroll.superfunds.read | Read a single superannuation funds record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/Superfunds/{id} | payroll.superfunds | Update or delete a single superannuation funds record | | GET, HEAD | /payroll.xro/1.0/LeaveApplications | payroll.employees.read | List leave applications | | POST, PUT | /payroll.xro/1.0/LeaveApplications | payroll.employees | Create or update leave applications | | GET, HEAD | /payroll.xro/1.0/LeaveApplications/{id} | payroll.employees.read | Read a single leave applications record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/LeaveApplications/{id} | payroll.employees | Update or delete a single leave applications record | | GET, HEAD | /payroll.xro/1.0/PayrollCalendars | payroll.settings.read | List payroll calendars | | POST, PUT | /payroll.xro/1.0/PayrollCalendars | payroll.settings | Create or update payroll calendars | | GET, HEAD | /payroll.xro/1.0/PayrollCalendars/{id} | payroll.settings.read | Read a single payroll calendars record | | POST, PUT, PATCH, DELETE | /payroll.xro/1.0/PayrollCalendars/{id} | payroll.settings | Update or delete a single payroll calendars record | | GET, HEAD | /payroll.xro/1.0/SuperfundProducts | payroll.superfundproducts.read | Read the superannuation fund product register | **Payroll AU** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /payroll.xro/2.0/Employees | payroll.employees.read | List employees | | POST, PUT | /payroll.xro/2.0/Employees | payroll.employees | Create or update employees | | GET, HEAD | /payroll.xro/2.0/Employees/{id} | payroll.employees.read | Read a single employees record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id} | payroll.employees | Update or delete a single employees record | | GET, HEAD | /payroll.xro/2.0/PayRuns | payroll.payruns.read | List pay runs | | POST, PUT | /payroll.xro/2.0/PayRuns | payroll.payruns | Create or update pay runs | | GET, HEAD | /payroll.xro/2.0/PayRuns/{id} | payroll.payruns.read | Read a single pay runs record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/PayRuns/{id} | payroll.payruns | Update or delete a single pay runs record | | GET, HEAD | /payroll.xro/2.0/Timesheets | payroll.timesheets.read | List timesheets | | POST, PUT | /payroll.xro/2.0/Timesheets | payroll.timesheets | Create or update timesheets | | GET, HEAD | /payroll.xro/2.0/Timesheets/{id} | payroll.timesheets.read | Read a single timesheets record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Timesheets/{id} | payroll.timesheets | Update or delete a single timesheets record | | GET, HEAD | /payroll.xro/2.0/Payslips | payroll.payslip.read | List pay slips | | POST, PUT | /payroll.xro/2.0/Payslips | payroll.payslip | Create or update pay slips | | GET, HEAD | /payroll.xro/2.0/Payslips/{id} | payroll.payslip.read | Read a single pay slips record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Payslips/{id} | payroll.payslip | Update or delete a single pay slips record | | GET, HEAD | /payroll.xro/2.0/PayRunCalendars | payroll.settings.read | List pay run calendars | | POST, PUT | /payroll.xro/2.0/PayRunCalendars | payroll.settings | Create or update pay run calendars | | GET, HEAD | /payroll.xro/2.0/PayRunCalendars/{id} | payroll.settings.read | Read a single pay run calendars record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/PayRunCalendars/{id} | payroll.settings | Update or delete a single pay run calendars record | | GET, HEAD | /payroll.xro/2.0/PayItems | payroll.settings.read | List pay items | | POST, PUT | /payroll.xro/2.0/PayItems | payroll.settings | Create or update pay items | | GET, HEAD | /payroll.xro/2.0/PayItems/{id} | payroll.settings.read | Read a single pay items record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/PayItems/{id} | payroll.settings | Update or delete a single pay items record | | GET, HEAD | /payroll.xro/2.0/LeaveTypes | payroll.settings.read | List leave types | | POST, PUT | /payroll.xro/2.0/LeaveTypes | payroll.settings | Create or update leave types | | GET, HEAD | /payroll.xro/2.0/LeaveTypes/{id} | payroll.settings.read | Read a single leave types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/LeaveTypes/{id} | payroll.settings | Update or delete a single leave types record | | GET, HEAD | /payroll.xro/2.0/Settings | payroll.settings.read | List payroll settings | | POST, PUT | /payroll.xro/2.0/Settings | payroll.settings | Create or update payroll settings | | GET, HEAD | /payroll.xro/2.0/Settings/{id} | payroll.settings.read | Read a single payroll settings record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Settings/{id} | payroll.settings | Update or delete a single payroll settings record | | GET, HEAD | /payroll.xro/2.0/Superfunds | payroll.superfunds.read | List superannuation funds | | POST, PUT | /payroll.xro/2.0/Superfunds | payroll.superfunds | Create or update superannuation funds | | GET, HEAD | /payroll.xro/2.0/Superfunds/{id} | payroll.superfunds.read | Read a single superannuation funds record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Superfunds/{id} | payroll.superfunds | Update or delete a single superannuation funds record | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/Leave | payroll.employees.read | Read leave for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/Leave | payroll.employees | Create or update leave for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/Leave/{subId} | payroll.employees.read | Read a single leave record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/Leave/{subId} | payroll.employees | Update or delete a single leave record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeaveBalances | payroll.employees.read | Read leave balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/LeaveBalances | payroll.employees | Create or update leave balances for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeaveBalances/{subId} | payroll.employees.read | Read a single leave balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/LeaveBalances/{subId} | payroll.employees | Update or delete a single leave balances record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeavePeriods | payroll.employees.read | Read leave periods for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/LeavePeriods | payroll.employees | Create or update leave periods for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeavePeriods/{subId} | payroll.employees.read | Read a single leave periods record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/LeavePeriods/{subId} | payroll.employees | Update or delete a single leave periods record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeaveSetup | payroll.employees.read | Read leave setup for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/LeaveSetup | payroll.employees | Create or update leave setup for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/LeaveSetup/{subId} | payroll.employees.read | Read a single leave setup record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/LeaveSetup/{subId} | payroll.employees | Update or delete a single leave setup record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/OpeningBalances | payroll.employees.read | Read opening balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/OpeningBalances | payroll.employees | Create or update opening balances for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/OpeningBalances/{subId} | payroll.employees.read | Read a single opening balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/OpeningBalances/{subId} | payroll.employees | Update or delete a single opening balances record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/PayTemplates | payroll.employees.read | Read pay templates for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/PayTemplates | payroll.employees | Create or update pay templates for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/PayTemplates/{subId} | payroll.employees.read | Read a single pay templates record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/PayTemplates/{subId} | payroll.employees | Update or delete a single pay templates record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/SalaryAndWages | payroll.employees.read | Read salary and wages for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/SalaryAndWages | payroll.employees | Create or update salary and wages for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/SalaryAndWages/{subId} | payroll.employees.read | Read a single salary and wages record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/SalaryAndWages/{subId} | payroll.employees | Update or delete a single salary and wages record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/Tax | payroll.employees.read | Read tax for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/Tax | payroll.employees | Create or update tax for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/Tax/{subId} | payroll.employees.read | Read a single tax record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/Tax/{subId} | payroll.employees | Update or delete a single tax record for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/SuperMemberships | payroll.employees.read | Read super memberships for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/Employees/{id}/SuperMemberships | payroll.employees | Create or update super memberships for an employee | | GET, HEAD | /payroll.xro/2.0/Employees/{id}/SuperMemberships/{subId} | payroll.employees.read | Read a single super memberships record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/Employees/{id}/SuperMemberships/{subId} | payroll.employees | Update or delete a single super memberships record for an employee | **Payroll UK** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /payroll.xro/2.0/uk/Employees | payroll_uk.employees.read | List employees | | POST, PUT | /payroll.xro/2.0/uk/Employees | payroll_uk.employees | Create or update employees | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id} | payroll_uk.employees.read | Read a single employees record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id} | payroll_uk.employees | Update or delete a single employees record | | GET, HEAD | /payroll.xro/2.0/uk/PayRuns | payroll_uk.payruns.read | List pay runs | | POST, PUT | /payroll.xro/2.0/uk/PayRuns | payroll_uk.payruns | Create or update pay runs | | GET, HEAD | /payroll.xro/2.0/uk/PayRuns/{id} | payroll_uk.payruns.read | Read a single pay runs record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/PayRuns/{id} | payroll_uk.payruns | Update or delete a single pay runs record | | GET, HEAD | /payroll.xro/2.0/uk/Payslips | payroll_uk.payslip.read | List pay slips | | POST, PUT | /payroll.xro/2.0/uk/Payslips | payroll_uk.payslip | Create or update pay slips | | GET, HEAD | /payroll.xro/2.0/uk/Payslips/{id} | payroll_uk.payslip.read | Read a single pay slips record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Payslips/{id} | payroll_uk.payslip | Update or delete a single pay slips record | | GET, HEAD | /payroll.xro/2.0/uk/Timesheets | payroll_uk.timesheets.read | List timesheets | | POST, PUT | /payroll.xro/2.0/uk/Timesheets | payroll_uk.timesheets | Create or update timesheets | | GET, HEAD | /payroll.xro/2.0/uk/Timesheets/{id} | payroll_uk.timesheets.read | Read a single timesheets record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Timesheets/{id} | payroll_uk.timesheets | Update or delete a single timesheets record | | GET, HEAD | /payroll.xro/2.0/uk/Settings | payroll_uk.settings.read | List payroll settings | | POST, PUT | /payroll.xro/2.0/uk/Settings | payroll_uk.settings | Create or update payroll settings | | GET, HEAD | /payroll.xro/2.0/uk/Settings/{id} | payroll_uk.settings.read | Read a single payroll settings record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Settings/{id} | payroll_uk.settings | Update or delete a single payroll settings record | | GET, HEAD | /payroll.xro/2.0/uk/PayRunCalendars | payroll_uk.settings.read | List pay run calendars | | POST, PUT | /payroll.xro/2.0/uk/PayRunCalendars | payroll_uk.settings | Create or update pay run calendars | | GET, HEAD | /payroll.xro/2.0/uk/PayRunCalendars/{id} | payroll_uk.settings.read | Read a single pay run calendars record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/PayRunCalendars/{id} | payroll_uk.settings | Update or delete a single pay run calendars record | | GET, HEAD | /payroll.xro/2.0/uk/Earnings | payroll_uk.settings.read | List earnings rates | | POST, PUT | /payroll.xro/2.0/uk/Earnings | payroll_uk.settings | Create or update earnings rates | | GET, HEAD | /payroll.xro/2.0/uk/Earnings/{id} | payroll_uk.settings.read | Read a single earnings rates record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Earnings/{id} | payroll_uk.settings | Update or delete a single earnings rates record | | GET, HEAD | /payroll.xro/2.0/uk/Deductions | payroll_uk.settings.read | List deduction types | | POST, PUT | /payroll.xro/2.0/uk/Deductions | payroll_uk.settings | Create or update deduction types | | GET, HEAD | /payroll.xro/2.0/uk/Deductions/{id} | payroll_uk.settings.read | Read a single deduction types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Deductions/{id} | payroll_uk.settings | Update or delete a single deduction types record | | GET, HEAD | /payroll.xro/2.0/uk/LeaveTypes | payroll_uk.settings.read | List leave types | | POST, PUT | /payroll.xro/2.0/uk/LeaveTypes | payroll_uk.settings | Create or update leave types | | GET, HEAD | /payroll.xro/2.0/uk/LeaveTypes/{id} | payroll_uk.settings.read | Read a single leave types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/LeaveTypes/{id} | payroll_uk.settings | Update or delete a single leave types record | | GET, HEAD | /payroll.xro/2.0/uk/Reimbursements | payroll_uk.settings.read | List reimbursement types | | POST, PUT | /payroll.xro/2.0/uk/Reimbursements | payroll_uk.settings | Create or update reimbursement types | | GET, HEAD | /payroll.xro/2.0/uk/Reimbursements/{id} | payroll_uk.settings.read | Read a single reimbursement types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Reimbursements/{id} | payroll_uk.settings | Update or delete a single reimbursement types record | | GET, HEAD | /payroll.xro/2.0/uk/Benefits | payroll_uk.settings.read | List benefits | | POST, PUT | /payroll.xro/2.0/uk/Benefits | payroll_uk.settings | Create or update benefits | | GET, HEAD | /payroll.xro/2.0/uk/Benefits/{id} | payroll_uk.settings.read | Read a single benefits record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Benefits/{id} | payroll_uk.settings | Update or delete a single benefits record | | GET, HEAD | /payroll.xro/2.0/uk/StatutoryLeaves | payroll_uk.employees.read | List statutory leave | | POST, PUT | /payroll.xro/2.0/uk/StatutoryLeaves | payroll_uk.employees | Create or update statutory leave | | GET, HEAD | /payroll.xro/2.0/uk/StatutoryLeaves/{id} | payroll_uk.employees.read | Read a single statutory leave record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/StatutoryLeaves/{id} | payroll_uk.employees | Update or delete a single statutory leave record | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Employment | payroll_uk.employees.read | Read employment for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/Employment | payroll_uk.employees | Create or update employment for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Employment/{subId} | payroll_uk.employees.read | Read a single employment record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/Employment/{subId} | payroll_uk.employees | Update or delete a single employment record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Tax | payroll_uk.employees.read | Read tax for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/Tax | payroll_uk.employees | Create or update tax for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Tax/{subId} | payroll_uk.employees.read | Read a single tax record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/Tax/{subId} | payroll_uk.employees | Update or delete a single tax record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Leave | payroll_uk.employees.read | Read leave for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/Leave | payroll_uk.employees | Create or update leave for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Leave/{subId} | payroll_uk.employees.read | Read a single leave record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/Leave/{subId} | payroll_uk.employees | Update or delete a single leave record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/LeaveBalances | payroll_uk.employees.read | Read leave balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/LeaveBalances | payroll_uk.employees | Create or update leave balances for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/LeaveBalances/{subId} | payroll_uk.employees.read | Read a single leave balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/LeaveBalances/{subId} | payroll_uk.employees | Update or delete a single leave balances record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/LeavePeriods | payroll_uk.employees.read | Read leave periods for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/LeavePeriods | payroll_uk.employees | Create or update leave periods for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/LeavePeriods/{subId} | payroll_uk.employees.read | Read a single leave periods record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/LeavePeriods/{subId} | payroll_uk.employees | Update or delete a single leave periods record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/OpeningBalances | payroll_uk.employees.read | Read opening balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/OpeningBalances | payroll_uk.employees | Create or update opening balances for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/OpeningBalances/{subId} | payroll_uk.employees.read | Read a single opening balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/OpeningBalances/{subId} | payroll_uk.employees | Update or delete a single opening balances record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/PayTemplates | payroll_uk.employees.read | Read pay templates for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/PayTemplates | payroll_uk.employees | Create or update pay templates for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/PayTemplates/{subId} | payroll_uk.employees.read | Read a single pay templates record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/PayTemplates/{subId} | payroll_uk.employees | Update or delete a single pay templates record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/SalaryAndWages | payroll_uk.employees.read | Read salary and wages for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/SalaryAndWages | payroll_uk.employees | Create or update salary and wages for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/SalaryAndWages/{subId} | payroll_uk.employees.read | Read a single salary and wages record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/SalaryAndWages/{subId} | payroll_uk.employees | Update or delete a single salary and wages record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/WorkingPattern | payroll_uk.employees.read | Read working pattern for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/WorkingPattern | payroll_uk.employees | Create or update working pattern for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/WorkingPattern/{subId} | payroll_uk.employees.read | Read a single working pattern record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/WorkingPattern/{subId} | payroll_uk.employees | Update or delete a single working pattern record for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Benefits | payroll_uk.employees.read | Read benefits for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/uk/Employees/{id}/Benefits | payroll_uk.employees | Create or update benefits for an employee | | GET, HEAD | /payroll.xro/2.0/uk/Employees/{id}/Benefits/{subId} | payroll_uk.employees.read | Read a single benefits record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/uk/Employees/{id}/Benefits/{subId} | payroll_uk.employees | Update or delete a single benefits record for an employee | | POST, PUT | /payroll.xro/2.0/uk/Timesheets/{id}/Lines | payroll_uk.timesheets | Add a line to a UK timesheet | | POST, PUT, DELETE | /payroll.xro/2.0/uk/Timesheets/{id}/Lines/{lineId} | payroll_uk.timesheets | Update or delete a UK timesheet line | **Payroll NZ** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /payroll.xro/2.0/nz/Employees | payroll_nz.employees.read | List employees | | POST, PUT | /payroll.xro/2.0/nz/Employees | payroll_nz.employees | Create or update employees | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id} | payroll_nz.employees.read | Read a single employees record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id} | payroll_nz.employees | Update or delete a single employees record | | GET, HEAD | /payroll.xro/2.0/nz/PayRuns | payroll_nz.payruns.read | List pay runs | | POST, PUT | /payroll.xro/2.0/nz/PayRuns | payroll_nz.payruns | Create or update pay runs | | GET, HEAD | /payroll.xro/2.0/nz/PayRuns/{id} | payroll_nz.payruns.read | Read a single pay runs record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/PayRuns/{id} | payroll_nz.payruns | Update or delete a single pay runs record | | GET, HEAD | /payroll.xro/2.0/nz/Payslips | payroll_nz.payslip.read | List pay slips | | POST, PUT | /payroll.xro/2.0/nz/Payslips | payroll_nz.payslip | Create or update pay slips | | GET, HEAD | /payroll.xro/2.0/nz/Payslips/{id} | payroll_nz.payslip.read | Read a single pay slips record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Payslips/{id} | payroll_nz.payslip | Update or delete a single pay slips record | | GET, HEAD | /payroll.xro/2.0/nz/Timesheets | payroll_nz.timesheets.read | List timesheets | | POST, PUT | /payroll.xro/2.0/nz/Timesheets | payroll_nz.timesheets | Create or update timesheets | | GET, HEAD | /payroll.xro/2.0/nz/Timesheets/{id} | payroll_nz.timesheets.read | Read a single timesheets record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Timesheets/{id} | payroll_nz.timesheets | Update or delete a single timesheets record | | GET, HEAD | /payroll.xro/2.0/nz/Settings | payroll_nz.settings.read | List payroll settings | | POST, PUT | /payroll.xro/2.0/nz/Settings | payroll_nz.settings | Create or update payroll settings | | GET, HEAD | /payroll.xro/2.0/nz/Settings/{id} | payroll_nz.settings.read | Read a single payroll settings record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Settings/{id} | payroll_nz.settings | Update or delete a single payroll settings record | | GET, HEAD | /payroll.xro/2.0/nz/PayRunCalendars | payroll_nz.settings.read | List pay run calendars | | POST, PUT | /payroll.xro/2.0/nz/PayRunCalendars | payroll_nz.settings | Create or update pay run calendars | | GET, HEAD | /payroll.xro/2.0/nz/PayRunCalendars/{id} | payroll_nz.settings.read | Read a single pay run calendars record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/PayRunCalendars/{id} | payroll_nz.settings | Update or delete a single pay run calendars record | | GET, HEAD | /payroll.xro/2.0/nz/EarningsRates | payroll_nz.settings.read | List earnings rates | | POST, PUT | /payroll.xro/2.0/nz/EarningsRates | payroll_nz.settings | Create or update earnings rates | | GET, HEAD | /payroll.xro/2.0/nz/EarningsRates/{id} | payroll_nz.settings.read | Read a single earnings rates record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/EarningsRates/{id} | payroll_nz.settings | Update or delete a single earnings rates record | | GET, HEAD | /payroll.xro/2.0/nz/Deductions | payroll_nz.settings.read | List deduction types | | POST, PUT | /payroll.xro/2.0/nz/Deductions | payroll_nz.settings | Create or update deduction types | | GET, HEAD | /payroll.xro/2.0/nz/Deductions/{id} | payroll_nz.settings.read | Read a single deduction types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Deductions/{id} | payroll_nz.settings | Update or delete a single deduction types record | | GET, HEAD | /payroll.xro/2.0/nz/LeaveTypes | payroll_nz.settings.read | List leave types | | POST, PUT | /payroll.xro/2.0/nz/LeaveTypes | payroll_nz.settings | Create or update leave types | | GET, HEAD | /payroll.xro/2.0/nz/LeaveTypes/{id} | payroll_nz.settings.read | Read a single leave types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/LeaveTypes/{id} | payroll_nz.settings | Update or delete a single leave types record | | GET, HEAD | /payroll.xro/2.0/nz/Reimbursements | payroll_nz.settings.read | List reimbursement types | | POST, PUT | /payroll.xro/2.0/nz/Reimbursements | payroll_nz.settings | Create or update reimbursement types | | GET, HEAD | /payroll.xro/2.0/nz/Reimbursements/{id} | payroll_nz.settings.read | Read a single reimbursement types record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Reimbursements/{id} | payroll_nz.settings | Update or delete a single reimbursement types record | | GET, HEAD | /payroll.xro/2.0/nz/Superannuations | payroll_nz.settings.read | List superannuation schemes | | POST, PUT | /payroll.xro/2.0/nz/Superannuations | payroll_nz.settings | Create or update superannuation schemes | | GET, HEAD | /payroll.xro/2.0/nz/Superannuations/{id} | payroll_nz.settings.read | Read a single superannuation schemes record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Superannuations/{id} | payroll_nz.settings | Update or delete a single superannuation schemes record | | GET, HEAD | /payroll.xro/2.0/nz/StatutoryDeductions | payroll_nz.settings.read | List statutory deductions | | POST, PUT | /payroll.xro/2.0/nz/StatutoryDeductions | payroll_nz.settings | Create or update statutory deductions | | GET, HEAD | /payroll.xro/2.0/nz/StatutoryDeductions/{id} | payroll_nz.settings.read | Read a single statutory deductions record | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/StatutoryDeductions/{id} | payroll_nz.settings | Update or delete a single statutory deductions record | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Employment | payroll_nz.employees.read | Read employment for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/Employment | payroll_nz.employees | Create or update employment for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Employment/{subId} | payroll_nz.employees.read | Read a single employment record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/Employment/{subId} | payroll_nz.employees | Update or delete a single employment record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Tax | payroll_nz.employees.read | Read tax for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/Tax | payroll_nz.employees | Create or update tax for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Tax/{subId} | payroll_nz.employees.read | Read a single tax record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/Tax/{subId} | payroll_nz.employees | Update or delete a single tax record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Leave | payroll_nz.employees.read | Read leave for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/Leave | payroll_nz.employees | Create or update leave for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/Leave/{subId} | payroll_nz.employees.read | Read a single leave record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/Leave/{subId} | payroll_nz.employees | Update or delete a single leave record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeaveBalances | payroll_nz.employees.read | Read leave balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/LeaveBalances | payroll_nz.employees | Create or update leave balances for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeaveBalances/{subId} | payroll_nz.employees.read | Read a single leave balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/LeaveBalances/{subId} | payroll_nz.employees | Update or delete a single leave balances record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeavePeriods | payroll_nz.employees.read | Read leave periods for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/LeavePeriods | payroll_nz.employees | Create or update leave periods for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeavePeriods/{subId} | payroll_nz.employees.read | Read a single leave periods record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/LeavePeriods/{subId} | payroll_nz.employees | Update or delete a single leave periods record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeaveSetup | payroll_nz.employees.read | Read leave setup for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/LeaveSetup | payroll_nz.employees | Create or update leave setup for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/LeaveSetup/{subId} | payroll_nz.employees.read | Read a single leave setup record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/LeaveSetup/{subId} | payroll_nz.employees | Update or delete a single leave setup record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/OpeningBalances | payroll_nz.employees.read | Read opening balances for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/OpeningBalances | payroll_nz.employees | Create or update opening balances for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/OpeningBalances/{subId} | payroll_nz.employees.read | Read a single opening balances record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/OpeningBalances/{subId} | payroll_nz.employees | Update or delete a single opening balances record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/PayTemplates | payroll_nz.employees.read | Read pay templates for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/PayTemplates | payroll_nz.employees | Create or update pay templates for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/PayTemplates/{subId} | payroll_nz.employees.read | Read a single pay templates record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/PayTemplates/{subId} | payroll_nz.employees | Update or delete a single pay templates record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/SalaryAndWages | payroll_nz.employees.read | Read salary and wages for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/SalaryAndWages | payroll_nz.employees | Create or update salary and wages for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/SalaryAndWages/{subId} | payroll_nz.employees.read | Read a single salary and wages record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/SalaryAndWages/{subId} | payroll_nz.employees | Update or delete a single salary and wages record for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/WorkingPattern | payroll_nz.employees.read | Read working pattern for an employee | | POST, PUT, PATCH | /payroll.xro/2.0/nz/Employees/{id}/WorkingPattern | payroll_nz.employees | Create or update working pattern for an employee | | GET, HEAD | /payroll.xro/2.0/nz/Employees/{id}/WorkingPattern/{subId} | payroll_nz.employees.read | Read a single working pattern record for an employee | | POST, PUT, PATCH, DELETE | /payroll.xro/2.0/nz/Employees/{id}/WorkingPattern/{subId} | payroll_nz.employees | Update or delete a single working pattern record for an employee | **Files** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /files.xro/1.0/Files | files.read | List files | | POST, PUT | /files.xro/1.0/Files | files | Create files | | GET, HEAD | /files.xro/1.0/Files/{id} | files.read | Read a single files record | | POST, PUT, PATCH, DELETE | /files.xro/1.0/Files/{id} | files | Update or delete a single files record | | GET, HEAD | /files.xro/1.0/Folders | files.read | List folders | | POST, PUT | /files.xro/1.0/Folders | files | Create folders | | GET, HEAD | /files.xro/1.0/Folders/{id} | files.read | Read a single folders record | | POST, PUT, PATCH, DELETE | /files.xro/1.0/Folders/{id} | files | Update or delete a single folders record | | GET, HEAD | /files.xro/1.0/Associations | files.read | List file associations | | POST, PUT | /files.xro/1.0/Associations | files | Create file associations | | GET, HEAD | /files.xro/1.0/Associations/{id} | files.read | Read a single file associations record | | POST, PUT, PATCH, DELETE | /files.xro/1.0/Associations/{id} | files | Update or delete a single file associations record | | GET, HEAD | /files.xro/1.0/Files/{id}/Content | files.read | Download file content | | GET, HEAD | /files.xro/1.0/Files/{id}/Associations | files.read | List what a file is associated with | | POST, PUT | /files.xro/1.0/Files/{id}/Associations | files | Associate a file with an accounting record | | DELETE | /files.xro/1.0/Files/{id}/Associations/{objectId} | files | Remove one association from a file | | GET, HEAD | /files.xro/1.0/Associations/Count | files.read | Count associations for a set of objects | | GET, HEAD | /files.xro/1.0/Inbox | files.read | Read the file inbox folder | **Assets** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /assets.xro/1.0/Assets | assets.read | List fixed assets | | POST, PUT | /assets.xro/1.0/Assets | assets | Create fixed assets | | GET, HEAD | /assets.xro/1.0/Assets/{id} | assets.read | Read a single fixed assets record | | POST, PUT, PATCH, DELETE | /assets.xro/1.0/Assets/{id} | assets | Update or delete a single fixed assets record | | GET, HEAD | /assets.xro/1.0/AssetTypes | assets.read | List asset types | | POST, PUT | /assets.xro/1.0/AssetTypes | assets | Create or update an asset type | | GET, HEAD | /assets.xro/1.0/AssetTypes/{id} | assets.read | Read a single asset type | | GET, HEAD | /assets.xro/1.0/Settings | assets.read | Read fixed asset settings | **Projects** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /projects.xro/2.0/Projects | projects.read | List projects | | POST, PUT | /projects.xro/2.0/Projects | projects | Create projects | | GET, HEAD | /projects.xro/2.0/Projects/{id} | projects.read | Read a single projects record | | POST, PUT, PATCH, DELETE | /projects.xro/2.0/Projects/{id} | projects | Update or delete a single projects record | | GET, HEAD | /projects.xro/2.0/ProjectsUsers | projects.read | List project users | | POST, PUT | /projects.xro/2.0/ProjectsUsers | projects | Create project users | | GET, HEAD | /projects.xro/2.0/ProjectsUsers/{id} | projects.read | Read a single project users record | | POST, PUT, PATCH, DELETE | /projects.xro/2.0/ProjectsUsers/{id} | projects | Update or delete a single project users record | | GET, HEAD | /projects.xro/2.0/Tasks | projects.read | List tasks across projects | | GET, HEAD | /projects.xro/2.0/Time | projects.read | List time entries across projects | | GET, HEAD | /projects.xro/2.0/Projects/{id}/Tasks | projects.read | List tasks on a project | | POST | /projects.xro/2.0/Projects/{id}/Tasks | projects | Create a task on a project | | GET, HEAD | /projects.xro/2.0/Projects/{id}/Tasks/{taskId} | projects.read | Read a single task on a project | | PUT, PATCH, DELETE | /projects.xro/2.0/Projects/{id}/Tasks/{taskId} | projects | Update or delete a task on a project | | GET, HEAD | /projects.xro/2.0/Projects/{id}/Time | projects.read | List time entries on a project | | POST | /projects.xro/2.0/Projects/{id}/Time | projects | Create a time entry on a project | | GET, HEAD | /projects.xro/2.0/Projects/{id}/Time/{timeEntryId} | projects.read | Read a single time entry | | PUT, PATCH, DELETE | /projects.xro/2.0/Projects/{id}/Time/{timeEntryId} | projects | Update or delete a time entry | **Bank Feeds** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD, POST, DELETE | /bankfeeds.xro/1.0/FeedConnections | bankfeeds | Manage bank feed connections | | GET, HEAD, DELETE | /bankfeeds.xro/1.0/FeedConnections/{id} | bankfeeds | Read or delete a bank feed connection | | GET, HEAD, POST | /bankfeeds.xro/1.0/Statements | bankfeeds | Deliver or list bank feed statements | | GET, HEAD | /bankfeeds.xro/1.0/Statements/{id} | bankfeeds | Read a single bank feed statement | **Finance** | Methods | Path | Required scope | What it does | | --- | --- | --- | --- | | GET, HEAD | /finance.xro/1.0/AccountingActivity/* | finance.accountingactivity.read | Read accounting activity metrics | | GET, HEAD | /finance.xro/1.0/CashValidation | finance.cashvalidation.read | Read cash validation data | | GET, HEAD | /finance.xro/1.0/Statements/* | finance.statements.read | Read bank statements accounting data | | GET, HEAD | /finance.xro/1.0/BankStatementsPlus/* | finance.bankstatementsplus.read | Read enriched bank statement data | ## If an endpoint is missing An unmapped method and path returns `403 route_not_mapped` and is logged loudly on our side. That is the designed way to find gaps: a missing route is visible and refused, never quietly forwarded without a scope check. # Errors How to tell our refusal from Xero's, and what every proxy error code means. ## Proxy errors versus Xero errors Any response the proxy generated itself carries `X-Proxy-Error: true` and a stable `error.code`. Without that header, the response came from Xero and is exactly what Xero returned. ```json { "error": { "code": "scope_denied", "message": "This request requires the 'accounting.transactions' scope, which this key does not hold for this organisation.", "requiredScope": "accounting.transactions", "grantedScopes": ["accounting.transactions.read", "accounting.contacts.read"], "documentation": "/developers/errors", "requestId": "4f2c1e8a-9b3d-4c7e-a1f6-2d8b5e0c9a34" } } ``` `requestId` is on every refusal and on every success as `X-XeroProxy-Request-Id`. Log it: whoever administers the account can look that exact request up and see which gate produced this. See [diagnosing a request](/developers/diagnostics). ## Error codes | Code | Status | Cause | Fix | | --- | --- | --- | --- | | missing_credential | 401 | No Authorization header, or it was not a Bearer credential. | Send Authorization: Bearer xpk_live__. | | invalid_key | 401 | The key ID is unknown or the secret did not verify. | Check the credential was copied whole. Unknown key and wrong secret are deliberately indistinguishable. | | key_revoked | 401 | The key was revoked. This takes effect on the very next request. | Mint a replacement key. | | key_expired | 401 | The key passed its expiry, or its post-rotation grace window closed. | Use the replacement key issued at rotation. | | account_suspended | 403 | The owning account is suspended. | Contact the account owner. | | ip_not_allowed | 403 | The key has a network allowlist that does not include the calling address. | Add the address to the key's allowlist, or call from an allowed range. | | tenant_required | 400 | The key spans multiple organisations and no Xero-Tenant-Id header was sent. | Send Xero-Tenant-Id. The response lists which tenant IDs this key may reach. | | tenant_not_allowed | 403 | The requested tenant is not one this key may reach. | Use a tenant from the key's own set. GET /connections returns exactly that set. | | no_orgs | 403 | The key is not scoped to any active organisation. | Add an organisation to the key, or reconnect the Xero organisation. | | scope_denied | 403 | The endpoint requires a scope the key does not hold for this organisation. The response names the required scope and lists what the key holds. | Grant the scope on the key, and make sure the connection itself still holds it: the effective permission is the intersection of the two. | | route_not_mapped | 403 | The method and path are not in the route table. Unmapped routes are denied rather than forwarded. | Check the path against the route reference. If a legitimate Xero endpoint is missing, it needs adding to the table. | | connection_unavailable | 502 | The Xero connection behind this organisation is expired or disconnected. | Reconnect the organisation. Existing keys and org identity are preserved. | | upstream_unreachable | 502 | Xero could not be reached. | Retry with backoff. | | rate_limited | 429 | The key is over its own per-minute rate limit. The allowance refills continuously, so this clears on its own. | Honour Retry-After. X-XeroProxy-RateLimit-Remaining shows the budget on every response, so you can pace ahead of it. | | quota_exhausted | 429 | The key has spent its daily quota. Quotas reset at midnight UTC. | Wait for the reset, raise the key's quota, or cut the call count: reads served from the cache spend no quota at all. | | budget_reserved | 429 | The organisation is near its Xero daily limit and this key's priority stops above the remaining budget, so what is left stays available for higher-priority work. | Retry later, or have the account raise the key's priority. This is the account's own policy, not a limit Xero applied. | | idempotency_key_invalid | 400 | The Idempotency-Key header was empty, longer than 255 characters, or contained a space or a non-printable character. | Send a UUID, a ULID or any other printable ASCII token with no spaces. | | idempotency_conflict | 422 | This Idempotency-Key was already used for a different request. The response names the method and path it was first used for. | Generate a fresh key for each distinct write and reuse it only when retrying that same write. This refusal is protecting you: answering with the first request's response would have been silently wrong. | | idempotency_in_progress | 409 | An earlier request with this Idempotency-Key is still being processed. Sending it again is exactly the duplicate write the key exists to prevent. | Honour Retry-After. The retry receives the original request's result once it lands. | | idempotency_body_too_large | 413 | A request sent with an Idempotency-Key has to be read whole to be fingerprinted, and this one is over the account's limit. | Send it without the header to stream it through unprotected, or raise the limit on the write safety page. Attachments and file uploads are the usual case. | | deferred_not_found | 404 | No deferred request with that id was created by this API key. A job belonging to another key reads the same way, deliberately. | Use the id from the 202's X-XeroProxy-Deferred header, with the same credential that received it. GET /deferred lists everything this key has queued. | | deferred_failed | 502 | The work was accepted and could not be carried out: Xero stayed unreachable, or a key, scope or connection it depended on was withdrawn after it was queued. | Read the failure field for which of those it was. A withdrawn permission needs fixing before the request is worth making again; an unreachable Xero usually does not. | | deferred_expired | 410 | The request was accepted but the budget never freed up within the account's deferral window, so it was given up on. | A queue that expires work regularly is a quota that is too small for the traffic, not a scheduling problem. Raise the window, raise the limits, or send less. Re-sending a write needs a NEW Idempotency-Key: it is a new decision now. | | deferred_cancelled | 410 | The job was cancelled with DELETE /deferred/ before it was carried out. | Nothing to fix. Send the request again if it is still wanted. | | callback_token_invalid | 400 | X-XeroProxy-Callback-Token was empty, longer than 200 characters, or contained bytes that are not printable ASCII. | Send a short opaque identifier of your own choosing (your job reference, a uuid). It is echoed back on the completion callback and is not a credential. Checked on every request that carries it, not only ones that end up queued, so you find out now rather than when the callback arrives with nothing to match. | # Rate limits and quota What Xero limits, what we forward untouched, and how an account divides one shared budget between several applications. ## Passthrough behaviour Xero's own 429s pass straight through with `Retry-After` intact. The proxy does not queue, retry or absorb them, so your existing backoff keeps working exactly as it did against Xero directly. > This applies to Xero's refusals. A refusal *this platform* generated under a quota policy is a different thing, because the budget still exists and is merely spoken for: those can be queued and carried out when it frees up, if you ask. See [deferred execution](/developers/deferred). - `X-DayLimit-Remaining`, `X-MinLimit-Remaining` and `X-AppMinLimit-Remaining` are forwarded verbatim. - Every proxied request records those values, so the account can see how close the organisation is running to its ceiling. - A response served from cache carries none of them. Nothing was spent, and repeating the numbers from whenever it was stored would be a stale reading presented as a current one. ## The budget is shared Xero counts its limits against the organisation, roughly 60 calls a minute and 5,000 a day, and from Xero's side every application behind this platform is one app on one connection. That is the point of the platform and it is also its one hazard: without a policy the budget goes to whoever asks fastest, so a nightly export can leave an invoicing integration with nothing. > An account can divide that budget deliberately instead. Governance is off until the account turns it on, so unless yours has, nothing below applies to you and the proxy behaves exactly as this page described before. ## The three controls | Control | Applies to | What it does | | --- | --- | --- | | Rate limit | One key | A token bucket, refilling continuously. Stops one consumer monopolising the per-minute ceiling. Refusals return `rate_limited`. | | Daily quota | One key | A count against the UTC day, the same day boundary Xero's own limit uses. Refusals return `quota_exhausted`. | | Reserve | The organisation | Keys stop at a floor of the organisation's remaining Xero budget, by priority: bulk first, then standard, never critical. Refusals return `budget_reserved`. | Priority is set per key by the account: `critical` for the work that must not stop, `standard` by default, `bulk` for backfills and exports. It only matters as the day's budget runs down, which is exactly when it matters. ## What spends budget One rule: a request that reaches Xero spends budget, and a request that does not, does not. A read served from the cache spends no rate token, no daily quota and no reserve, and a request refused by any gate (a bad credential, a missing scope, an unmapped route) never counted in the first place. > That makes caching the cheapest way to stay inside a quota: the same reads still work and simply stop costing anything. `Cache-Control: no-cache` opts a single request out of the cache, and back into spending. ## Knowing where you stand When a limit applies to your key, every proxied response carries your current position. These are ours and are calculated per request, unlike the `X-...Limit-Remaining` set above, which is Xero's and is forwarded. | Header | Meaning | | --- | --- | | X-XeroProxy-RateLimit-Limit | Requests a minute this key is allowed. | | X-XeroProxy-RateLimit-Remaining | Whole requests available right now. The allowance refills continuously rather than resetting on a boundary. | | X-XeroProxy-RateLimit-Reset | Seconds until the allowance is back to full. | | X-XeroProxy-Quota-Limit | Requests this key may make in a UTC day. | | X-XeroProxy-Quota-Remaining | How many of them are left. | | X-XeroProxy-Quota-Reset | Seconds until midnight UTC. | | X-XeroProxy-Org-Day-Remaining | What Xero last reported the organisation has left today. Present when a reserve applies to your key. | | X-XeroProxy-Would-Throttle | Only in observe mode: this request went through, and names the limit that would have refused it. | ## Handling a throttle All three refusals are `429` with `Retry-After`, so a client that already backs off on Xero's 429s needs no changes. If you want to tell them apart, `X-Proxy-Error: true` marks ours and the code says which limit objected: ```json { "error": { "code": "budget_reserved", "message": "This organisation has 380 Xero calls left today and bulk keys stop below 1500, so the remainder stays available for higher-priority work. The budget resets at midnight UTC.", "retryAfterSeconds": 3600, "limit": "org-reserve", "documentation": "/developers/errors" } } ``` - `rate_limited` clears by itself within seconds. Retry-After is exact. - `quota_exhausted` clears at midnight UTC and not before. Retrying sooner wastes both our time and yours. - `budget_reserved` is the account's own policy, not Xero's. Retry-After is capped at an hour so that raising a key's priority takes effect promptly, but the budget itself only returns at midnight UTC. > A 429 without `X-Proxy-Error` came from Xero and means something different: the organisation's budget is genuinely gone, for everyone. There is a built-in alert rule for sustained Xero 429s, and another for an organisation running low before it gets there. ## Trying a policy before it bites An account can run the policy in observe mode. Every request is evaluated and the decision recorded, and nothing is refused. Callers see `X-XeroProxy-Would-Throttle` on requests an enforcing policy would have stopped, and the account sees the counts per key. It is the honest way to size limits: refusing a request Xero would have accepted changes the behaviour of software that is already shipped, and that is not a change to make on a guess. # MCP servers Two Model Context Protocol endpoints: a management control plane, and a public documentation server. ## Endpoints | Server | URL | Authentication | | --- | --- | --- | | Management | /api/mcp | Bearer API key carrying management scopes | | Documentation | /api/mcp/docs | None, public and read only | Both speak JSON-RPC 2.0 over HTTP POST and implement `initialize`, `tools/list` and `tools/call`. Point any MCP client at them: ```json { "mcpServers": { "xero-proxy": { "type": "http", "url": "https://xero-proxy.fullstackplus.com.au/api/mcp", "headers": { "Authorization": "Bearer xpk_live__" } }, "xero-proxy-docs": { "type": "http", "url": "https://xero-proxy.fullstackplus.com.au/api/mcp/docs" } } } ``` ## Management tools Every tool is confined to the account owning the calling key, and every write is recorded in the audit log with `origin: mcp`. A tool whose scope the key does not hold is not listed and cannot be called. - `list_connections`, `list_organisations`: connection health and the organisations behind it. - `list_keys`, `create_key`, `rotate_key`, `revoke_key`: the full key lifecycle, with the secret returned once on create and rotate. - `usage_summary`, `recent_requests`: request volume, error rate and the recent request log. - `list_alerts`, `acknowledge_alert`: the alert centre. - `cache_status`, `purge_cache`: what caching is saving, and dropping every stored response. - `quota_status`, `set_key_limits`: what each key is allowed and has spent, what Xero says is left, and changing a key's limits or priority. There is no tool for turning enforcement on: that is a decision a person makes, not one an agent makes to unblock itself. - `list_event_subscriptions`, `recent_event_deliveries`, `retry_event_delivery`: the webhook fan-out, its delivery history, and re-queuing one. ## Documentation tools - `search_docs`: ranked sections for a natural-language question. - `get_page`: one documentation page as markdown. - `resolve_route`: the required scope for a method and path, straight from the enforcement table. - `list_routes`: the route reference, optionally filtered by family. - `get_openapi`: the generated OpenAPI document. # Machine-readable docs OpenAPI, markdown variants and llms.txt, for tooling and for language models. ## What is available | Path | Format | Contents | | --- | --- | --- | | /openapi.json | OpenAPI 3.1 | Every mapped route, with its required scope | | /llms.txt | Markdown index | One line per page, with links | | /llms-full.txt | Markdown | The entire documentation set in one file | | /developers/.md | Markdown | Any documentation page as its source | | /api/docs/search | JSON | Ranked documentation sections for a query | Every page in this documentation set has a `.md` variant at the same path plus `.md`, and a copy button that puts the markdown on your clipboard. ## The OpenAPI document It is generated from the same route table the proxy enforces, so an endpoint that appears in it is an endpoint you can call, and the `x-required-scope` on each operation is the scope that will actually be checked. ```bash curl -s https://xero-proxy.fullstackplus.com.au/openapi.json | jq '.paths | keys | length' ``` # Alerting The built-in rules, how they are evaluated, and where notifications go. ## Built-in rules | Rule | Fires when | Default | | --- | --- | --- | | connection_expiry | A Xero connection is expiring, has expired, or was revoked at Xero. Every key on it stops working when it does. | 14 days of warning | | sustained_429 | Xero returned 429 more than the threshold within the window. The per-minute app ceiling is shared across everything on the connection. | 20 429s in the window | | unusual_key_usage | A key's request volume rose above a multiple of its own recent baseline, which is what a leaked credential looks like. | 5 times the baseline | | proxy_error_rate | The share of proxied requests failing exceeded the threshold. Usually a scope that was never granted, or a connection in trouble. | 25 percent of requests | | webhook_delivery_failures | Event deliveries to your own endpoints exhausted their retries. A subscriber that has quietly stopped accepting events is invisible from the endpoint's own side. | 5 exhausted deliveries in the window | | quota_pressure | An organisation is close to its Xero daily limit. The reserve decides who spends what is left, not how much is left, so this is the warning that buys time to act. | 750 calls left in the day | | idempotency_conflicts | A key sent an Idempotency-Key it had already used for a different write. The request was refused rather than answered wrongly, but an application generating keys that way is one code path from writing a duplicate. | 5 conflicts in the window | | deferred_backlog | Requests accepted for later execution have been waiting longer than expected. A queue that drains is the feature working; a queue that does not is work quietly not happening, which looks the same from outside until somebody asks where last week's invoices went. | 30 minutes the oldest job has waited | | callback_delivery_failures | Callbacks announcing that deferred work finished exhausted their retries. This one is worse than it looks: an endpoint that has stopped accepting them cannot tell, because nothing arriving is exactly what a quiet period looks like, and the software waiting on the signal removed its polling because the signal existed. | 5 exhausted deliveries in the window | ## Evaluation and delivery - Rules are evaluated on a schedule by a separate task container, never inside a request. - A condition that persists updates one alert rather than filling the page with duplicates, and records how many times it has been seen. - Delivery is by email or by webhook. A webhook receives a signed JSON payload; the signature is an HMAC of the body under your webhook secret. - Alerts remain in the dashboard whether or not delivery succeeded, and failed deliveries record why. # Read caching How repeat reads are served without spending Xero quota, what invalidates them, and how to opt out per request. ## Why it exists One connection serving many applications means they all spend the same organisation's Xero quota. Three apps polling the chart of accounts every minute cost three times what one does, for identical answers. Caching answers the repeats at the proxy. > It is off until an account turns it on, in the dashboard under Caching. Caching changes what a read returns, so it is never enabled on your behalf. ## What is cached | Request | Treatment | | --- | --- | | GET of reference data (accounts, tax rates, currencies, tracking categories, organisation, items, users, payroll settings) | Cached for the reference time limit, 1 hour by default | | GET of anything else that returned 200 | Cached for the transactional time limit, 60 seconds by default | | Attachments, file content, history endpoints | Never cached | | Conditional requests (If-None-Match, If-Modified-Since) | Passed straight to Xero | | Anything that is not a GET | Never cached, and clears its own family | | Responses larger than the size limit | Streamed through and never stored | The cache key covers the organisation, the path, the query (order-independent) and the `Accept` header, so a PDF and a JSON read of the same invoice are separate entries and two organisations can never share one. ## Invalidation A successful write clears everything cached for that organisation in the same API family, not just the resource you wrote. A payment changes an invoice and an invoice changes a contact's balance: chasing each of those with a narrower rule is how a cache ends up serving something quietly wrong. > Changes made in Xero directly, or by an application connected outside this platform, are invisible to the cache. Nothing here can see them, so the time limits are what bound how stale a read can be. Set them to match how fresh you actually need the data, and use 0 to turn off a class entirely. ## Controlling it per request | Header | Effect | | --- | --- | | `Cache-Control: no-cache` | Ignore any stored copy, fetch fresh, and store the result | | `Cache-Control: no-store` | Neither read nor write the cache for this request | Every response tells you what happened: `X-XeroProxy-Cache` is `hit`, `miss`, `store` or `bypass`, and a hit also carries `Age` and `X-XeroProxy-Cache-Expires`. ```bash curl -sD - -o /dev/null \ -H "Authorization: Bearer $XERO_PROXY_KEY" \ https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Accounts | grep -i x-xeroproxy-cache ``` - A cached response carries no rate-limit headers: nothing was spent at Xero, so repeating the numbers from when it was stored would be a stale reading presented as a current one. - Cached bodies are encrypted at rest with the same envelope scheme as connection tokens. - Every gate still applies. A cached body is only ever returned to a caller that just passed the same credential, network, tenant and scope checks it would have needed to fetch it. # Diagnosing a request Every response carries an id. What it is for, and how to get from a failure to the reason for it. ## The request id Every response this platform produces carries `X-XeroProxy-Request-Id`, and every refusal repeats it as `error.requestId` in the body. It is not a trace header borrowed from somewhere else: it is the primary key of that request's row in the account's log, so quoting it identifies exactly one request and nothing else. ```bash curl -sD - -o /dev/null \ -H "Authorization: Bearer $XERO_PROXY_KEY" \ https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices | grep -i x-xeroproxy-request-id ``` - Log it on every call your integration makes, successful or not. It costs one header and it is the difference between a support conversation and a guessing game. - It is generated before anything is checked, so even `missing_credential` - a refusal made before the proxy knows who you are - carries one. - Quote it to whoever administers the account. They can open it directly and see which gate answered, what it cost, and what caching, idempotency and the quota policy each decided. ## Working out what refused you The proxy answers a request in one of four ways, and the response tells you which without anybody having to look it up. | What you see | What happened | | --- | --- | | `X-Proxy-Error: true` | This platform refused it. `error.code` says which gate; the errors page lists all of them. It never reached Xero. | | `X-XeroProxy-Cache: hit` | Answered from a stored read. It never reached Xero and spent none of the organisation's Xero budget. | | `X-XeroProxy-Idempotent-Replay: true` | Answered from a recorded write. The write was not carried out again. | | None of the above | Forwarded to Xero. The status, headers and body are exactly what Xero returned. | > A 429 without `X-Proxy-Error` is Xero saying the organisation is out of budget. A 429 with it is this platform saying the budget exists and this key is not the one entitled to spend it. They need different responses, which is why they are distinguishable. ## When the metadata is not enough Request and response bodies are financial data and are not recorded. Occasionally a failure cannot be explained without them, so an account owner can turn on a capture window for a single key from the API keys page. - Per key, never account-wide, and lasting at most an hour. - It requires a written reason, which is stored against the key and in the audit log along with who turned it on. - Captured bodies are encrypted at rest, capped at 64 kB each, and expire on their own. - Credentials and `Idempotency-Key` are replaced with `[redacted]` before anything is written. No diagnosis needs the bearer token back, and an idempotency key would let whoever read it replay the write. - Turning capture off discards everything it collected, rather than leaving it to expire. > If you are asked to reproduce a failure while capture is on, do it with test data where you can. The window is short and the storage is encrypted, but the shortest-lived copy of a customer's ledger is still one that was never made. # Idempotent writes How to retry a write that timed out without creating it twice, and what the proxy does with an Idempotency-Key. ## Why it exists Everything above this page has a reason to tell you to try again. A quota policy returns 429. Xero returns its own 429s, forwarded untouched. A connection in trouble returns 502. A network drops a response after the request that produced it has already been carried out. On a read, retrying costs a little quota. On `POST /api.xro/2.0/Invoices` it is how an organisation ends up with the same invoice twice, and your application cannot prevent that on its own: from where it stands, *the answer never arrived* and *the write never happened* are the same observation. The proxy can tell the two apart, because it is the thing in the middle. Send an `Idempotency-Key` on a write and the attempt is recorded before it is forwarded and the answer kept once it returns, so a retry carrying the same key is answered from the record instead of being sent to Xero a second time. > It is off until an account turns it on, in the dashboard under Write safety. While it is off the header is forwarded to Xero untouched, exactly as it always was, so nothing changes for software already in production until somebody decides it should. ## Using it - Generate one key per distinct write, before the first attempt. A UUID is the usual choice; anything printable with no spaces and up to 255 characters works. - Reuse that key on every retry of that same write, and never for anything else. - It applies to POST, PUT, PATCH and DELETE. GET and HEAD are idempotent already and the header is ignored on them. ```bash KEY=$(uuidgen) # Both calls create ONE invoice. The second is answered from the record. for attempt in 1 2; do curl -sD - -o /dev/null -X POST https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices \ -H "Authorization: Bearer $XERO_PROXY_KEY" \ -H "Xero-Tenant-Id: $TENANT" \ -H "Idempotency-Key: $KEY" \ -H "Content-Type: application/json" \ -d '{"Type":"ACCREC","Contact":{"ContactID":"..."},"LineItems":[]}' \ | grep -i x-xeroproxy-idempot done ``` The header is also forwarded to Xero, so Xero's own idempotency support still applies on the endpoints that have it. This is deliberately belt and braces: ours covers every endpoint and every failure mode, including the ones where the request never left this platform. ## What you get back | `X-XeroProxy-Idempotency` | What happened | | --- | --- | | `stored` | The write went to Xero and its answer was recorded. A retry with this key gets that answer. | | `replay` | The write was NOT carried out again. The body is the answer the original attempt produced, and `X-XeroProxy-Idempotent-Replay: true` is set alongside it. | | `released` | Nothing worth replaying came back (a 429, a 5xx, or Xero was never reached). The record was dropped, so your retry is a first attempt again. | | `off` | Idempotent writes are not enabled on this account. The key was forwarded to Xero and nothing else was done with it. | A replay carries no rate-limit or budget headers, and spends no rate token, no daily quota and no reserve. It reached no Xero endpoint, so it costs nothing, exactly like a cache hit. ## When it refuses | Code | Status | Meaning | | --- | --- | --- | | `idempotency_in_progress` | 409 | The first attempt is still running. Sending it again is the duplicate the key exists to prevent, so it is refused with a `Retry-After` rather than forwarded. | | `idempotency_conflict` | 422 | This key was already used for a different request. The response names the method and path it was first used for. | | `idempotency_body_too_large` | 413 | A request sent with a key has to be read whole to be fingerprinted, and this one is over the account's limit. Send it without the header, or raise the limit. | | `idempotency_key_invalid` | 400 | The header was empty, too long, or contained a space or a non-printable character. | > A conflict is protecting you. Answering a different request with the first one's response would be silently wrong, which is worse than the duplicate write this feature exists to prevent. If it fires repeatedly, the application is generating keys from something that is not the request, and the account gets an alert saying so. ## What is guaranteed, and what is not - The claim on a key is a single atomic insert, so two retries arriving at the same instant cannot both be treated as the first. - Only an answer is kept. A 429 or a 5xx means *ask again*, and storing one would turn a transient fault into a permanent wrong answer. - A 400 from Xero IS kept: repeating a malformed request produces the same refusal, and possibly a second record if it was only half malformed. - Records expire after the account's retention window, 24 hours by default. After that the same key is a first attempt again. - Stored bodies are encrypted at rest with the same envelope scheme as connection tokens. Only the status, the media type and the body are kept, never the rest of the response. - A container that dies mid-write leaves a claim behind; another attempt may take it over after the account's claim timeout, 90 seconds by default. - This protects a write through this proxy. It cannot deduplicate a write your application also makes through some other route into Xero. # Deferred execution Ask to be queued instead of refused: how a 429 becomes a 202 with a handle, and how to collect the answer. ## Why it exists A quota refusal is the right answer to *may I spend this budget right now* and the wrong answer to what you actually asked, which was *please carry this out*. The organisation's budget is not gone, it is spoken for: in ninety seconds, or at UTC midnight, it exists again. Your application cannot use that fact. It has a 429 and a `Retry-After`, and its only move is to sleep and ask again, holding the work in a process that may not be running by then. This proxy can use it. It holds the connection, watches the organisation's remaining budget, knows when every limit resets, knows the priority of each key competing for the same pool, and can carry out a write exactly once even across a restart. So a caller that opts in is answered `202 Accepted` with a handle instead of `429` with a `Retry-After`. The request is held and carried out when the budget it needs is there. > Nothing changes unless you ask. Deferral needs the account to enable it AND the individual request to carry `X-XeroProxy-Defer: allow`. Without both, a refusal is exactly the 429 it always was. Xero's own 429s are never deferred: they still pass straight through. ## Asking for it ```bash # Ask to be queued rather than refused. curl -sD - -o response.json \ "https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices?page=1" \ -H "Authorization: Bearer $XERO_PROXY_KEY" \ -H "Xero-Tenant-Id: $TENANT" \ -H "X-XeroProxy-Defer: allow" # 202 Accepted # X-XeroProxy-Deferred: 6f1c... <- the handle # Location: /deferred/6f1c... # Retry-After: 47 # Collect it. 202 means not yet; anything else IS the answer. curl -s "https://xero-proxy.fullstackplus.com.au/deferred/6f1c..." \ -H "Authorization: Bearer $XERO_PROXY_KEY" ``` A completed job hands back Xero's own status, content type and body, so whatever code you already wrote for the synchronous call handles the answer unchanged. `GET /deferred` lists everything this key has queued, and `DELETE /deferred/` cancels a job that has not started yet. > Everything is scoped to the API key that created the job, never to the account. Another application on the same account cannot read your stored response body, even though the account owner can see both jobs in the dashboard. ## Writes need an Idempotency-Key A write is deferred only when it can be guaranteed to happen exactly once. That means idempotent writes enabled on the account and an `Idempotency-Key` on the request. Without both, a write is refused with the ordinary 429 rather than queued. This is not a formality. A queue that might carry out a write twice is worse than no queue: it turns an error at your call site into a duplicate invoice found at reconciliation. The idempotency record is what lets a job survive a container dying mid-write, because the re-run finds the answer rather than repeating the request. - Retrying a deferred write with the same key returns the SAME job id rather than queueing a second one. The response says `duplicate: true`. - If budget frees up and your live retry goes through before the queue gets to it, the job notices and completes with that answer. The write still happens once. - You do not have to poll at all for a write: retrying it normally once the queue has run is answered from the idempotency record as a replay. ## States and refusals | State | What it means | | --- | --- | | `queued` | Accepted, waiting for budget. `GET` returns 202 with a `Retry-After`. | | `running` | Being carried out at Xero right now. Also a 202. | | `completed` | Done. The response is Xero's own, with `X-XeroProxy-Deferred-State: completed`. | | `failed` | Accepted and could not be carried out. `deferred_failed`, 502. | | `expired` | The budget never freed up within the account's window. `deferred_expired`, 410. | | `cancelled` | Cancelled before it started. `deferred_cancelled`, 410. | When a refusal could NOT be deferred and you asked for it, the 429 carries `X-XeroProxy-Defer` saying which one thing would have changed the answer: `not_enabled`, `writes_not_enabled`, `write_needs_idempotency`, `body_too_large` or `queue_full`. ## What is guaranteed, and what is not - Queued work spends budget when it runs, not when it was accepted, and it is ordered by the same key priority that governs live traffic. Queueing is not a way to get ahead of it. - Every gate is re-checked at execution against the state as it is then. Revoke a key, disconnect an organisation or withdraw a scope and its queued work fails rather than proceeding on a permission that no longer exists. - The queue is bounded per account. A full queue refuses with the ordinary 429 and `queue_full`, because a queue that grows forever hides a quota that is simply too small. - A job that waits longer than the account's window expires without running. Re-sending a write afterwards needs a new Idempotency-Key, because it is a new decision. - Unlike a live call, a job absorbs a 429 or a 5xx from Xero and tries again within its attempt budget. You already accepted that the answer arrives later, so waiting is what you asked for. - Stored request and response bodies are encrypted at rest with the same envelope scheme as connection tokens, and are deleted once the account's result window passes. - The 202 is not a promise that the request will succeed. It is a promise that it will be attempted, once, and that you can find out what happened. > You do not have to poll to find out. Register a callback endpoint and this proxy will tell you the moment a job reaches a terminal state. See [completion callbacks](/developers/callbacks). # Completion callbacks Be told when deferred work finishes, instead of polling for it: registering an endpoint, verifying the signature, and what a callback deliberately does not contain. ## Why it exists Deferred execution exists because a 429 leaves your application holding the work in a process that may not be running when the budget comes back. Handing you a `202` and a job id solves half of that. The work moved here; the *waiting* stayed with you. A caller that only has a handle has to keep a process alive, remember the id, and poll on a cadence it has to guess - which is the same process, the same handle and the same guessing the 429 required, now wearing a different status code. A job can also sit for the whole of your account's deferral window, so the poll loop is longer-lived than the one it replaced, not shorter. This proxy knows the instant a job reaches a terminal state. Register an endpoint and it says so. > Callbacks are additive. Polling `/deferred/` works exactly as it did and is still the way you collect the answer. What a callback removes is the need to *ask repeatedly whether there is one yet*. ## Registering an endpoint Endpoints are registered by an account owner on the **Callbacks** page in the dashboard, not by the API. This is deliberate and it is the security boundary of the whole feature: the URL decides where a message signed by this platform is sent, so a proxied request is never allowed to name it. If it could, every API key would be a way to make this proxy call an address of the caller's choosing with our signature attached. - The URL must be `https`, a fully qualified public hostname, and carry no credentials in the URL itself. The same check the event fan-out applies. - An endpoint hears about every key on the account by default, or you can restrict it to named keys. An account running an invoicing service and a reporting service can give each its own endpoint, and neither is told about the other's work. - An endpoint registered today hears about work that finishes from now on. It is not opened with a backlog of everything it missed. - Twenty consecutive failed deliveries pauses an endpoint. Pausing means *stop telling me*, not *hold them*: resuming does not replay what was skipped. ## Correlating a callback to your own record The job id is in every callback, and for a caller that stored it that is enough. When the process that receives callbacks is not the process that sent the request - which is the reason this feature exists - put your own identifier on the request instead and read it back: ```bash curl -sD - -o response.json \ "https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices?page=1" \ -H "Authorization: Bearer $XERO_PROXY_KEY" \ -H "Xero-Tenant-Id: $TENANT" \ -H "X-XeroProxy-Defer: allow" \ -H "X-XeroProxy-Callback-Token: run-2026-08-20-invoices" ``` Whatever you send comes back verbatim as `deferred.token`. It is an opaque correlation handle of at most 200 printable ASCII characters, it is stored in the clear, and it is sent over the network to your endpoint, so it is not a credential and must not be used as one. A malformed token is refused with `callback_token_invalid` on the request that sent it, rather than dropped and discovered missing hours later. > Retrying a deferred write with the same `Idempotency-Key` returns the job you already have, and the token stays the one that created it. There is one job, so there is one completion. ## What arrives A `POST` with a JSON body, sent when the job reaches `completed`, `failed`, `expired` or `cancelled`. ```json { "deliveryId": "9b2f...", "endpointId": "41a0...", "deferred": { "id": "6f1c...", "state": "completed", "request": { "method": "GET", "path": "/api.xro/2.0/Invoices" }, "keyId": "k_7f21a9", "reason": "org_reserve", "attempts": 1, "acceptedAt": "2026-08-20T04:11:07.000Z", "completedAt": "2026-08-20T04:13:41.000Z", "expiresAt": "2026-08-20T05:11:07.000Z", "status": 200, "failure": null, "token": "run-2026-08-20-invoices", "collect": "https://xero-proxy.fullstackplus.com.au/deferred/6f1c...", "documentation": "https://xero-proxy.fullstackplus.com.au/developers/callbacks" } } ``` `state: completed` means the request reached Xero and there is an answer; `status` is Xero's own status code for it, so a receiver can decide whether collecting it is worth a round trip. The other three states mean there is no answer, and `failure` says why. ## A callback never contains the response body This is a rule, not an omission, and it will not be relaxed. The answer to a deferred request belongs to the **API key that created the job**. That is what `/deferred/` enforces, and it is why another application on the same account cannot read your stored response body even though your account owner can see both jobs listed in the dashboard. A callback endpoint is registered by the **account owner**. Pushing the body to it would hand one application's financial data to an address chosen by somebody else, and would do it without the credential check that rule depends on. So the callback tells you the work finished and where the answer is; you collect it from `collect` with the same key that queued it, exactly as before. ## Verifying the signature Every callback carries `X-XeroProxy-Signature: t=,v1=`, an HMAC-SHA256 over `.` using your endpoint's signing secret. It is the same scheme the event fan-out uses, so one verifier covers both. ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody, header, secret) { const parts = Object.fromEntries( header.split(",").map((pair) => pair.split("=")), ); const expected = createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`, "utf8") .digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(parts.v1 ?? "", "hex"); if (a.length !== b.length || !timingSafeEqual(a, b)) return false; // The timestamp is inside the signed string, so a captured body cannot be // replayed later with a fresh one. Rejecting an old t is replay protection. return Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; } ``` > The secret is shown on the Callbacks page in the dashboard. It is derived rather than stored, so there is no copy of it at rest, and it is never returned by any API or MCP tool. ## Delivery, retries and what is guaranteed - Answer with any 2xx. Anything else, or a timeout after 10 seconds, is a failure. - Six attempts over about seven hours: 1 minute, 5, 15, 1 hour, then 6 hourly. `X-XeroProxy-Delivery` and `X-XeroProxy-Deferred` are stable across every attempt, so you can be idempotent without hashing the body. - At-least-once, not exactly-once. A receiver that answers slowly and then succeeds may be sent the same delivery again. - A callback is never the only record. If it never arrives, the job and its answer are still there to be collected until your account's result window passes, and the account owner sees every delivery attempt in the dashboard. - Deliveries are queued and sent by a scheduled task, never on the request path, so a slow endpoint of yours never slows a proxied call of yours. > An endpoint that has quietly stopped accepting callbacks is invisible from its own side, and the software waiting on them has by design stopped polling. That is what the **Completion callbacks failing** alert rule is for; leave it enabled. # Event webhooks Xero events fanned out to as many of your own endpoints as you like, signed, retried and deduplicated. ## Why it exists Xero delivers webhooks to one endpoint per application, which is the same bottleneck this platform exists to remove for API access. Events arrive here once, are matched to the organisation they belong to, and are delivered to every endpoint you have registered for that organisation and category. ## Subscribing - Add an endpoint in the dashboard under Event webhooks. It must be https and publicly resolvable. - Filter by event category (`INVOICE`, `CONTACT`, `SUBSCRIPTION`) or take everything, including categories Xero adds later. - Filter by organisation, or take every organisation on the account, including ones connected later. - Each subscription gets its own signing secret, its own retry queue and its own failure count. ## The delivery One event per request. The resource URL is rewritten onto this proxy, so the endpoint can fetch it with the API key it already holds rather than needing a Xero connection of its own. ```json { "deliveryId": "…", "subscriptionId": "…", "event": { "id": "…", "category": "INVOICE", "type": "UPDATE", "tenantId": "…", "resourceId": "…", "resourceUrl": "https://xero-proxy.fullstackplus.com.au/api.xro/2.0/Invoices/", "eventDate": "2026-08-18T04:00:00.000Z", "receivedAt": "2026-08-18T04:00:01.000Z" } } ``` | Header | Meaning | | --- | --- | | `X-XeroProxy-Signature` | `t=,v1=`: HMAC-SHA256 of `.` under the subscription secret | | `X-XeroProxy-Delivery` | Stable across retries: the idempotency key for your handler | | `X-XeroProxy-Event` | The event this delivery carries; one event can go to several subscriptions | | `X-XeroProxy-Tenant` | The Xero tenant id the event belongs to | ## Verifying a delivery Verify before parsing, compare in constant time, and reject an old timestamp: because the timestamp is inside the signed string, a captured body cannot be replayed with a fresh one. ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; export function verify(rawBody, header, secret) { const parts = Object.fromEntries( header.split(",").map((part) => part.split("=")), ); const age = Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t)); if (!Number.isFinite(age) || age > 300) return false; const expected = createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`, "utf8") .digest(); const presented = Buffer.from(parts.v1 ?? "", "hex"); return presented.length === expected.length && timingSafeEqual(presented, expected); } ``` ## Retries and duplicates - Answer 2xx quickly. Anything else is retried six times over roughly seven hours: after 1, 5 and 15 minutes, then 1, 6 and 6 hours. - An endpoint that fails 20 deliveries in a row is paused rather than retried forever. Events keep being recorded; re-enable it in the dashboard and re-queue what you need. - Xero redelivers what it did not get a prompt answer for, so events are deduplicated on arrival. Deliveries are unique per subscription and event, which makes the fan-out safe to re-run. - Delivery is at least once. Use the delivery id, or the event id, as your idempotency key. - Events are kept for 30 days, then pruned along with their delivery history. > An endpoint that has quietly stopped accepting deliveries is invisible from its own side, so the `webhook_delivery_failures` alert rule watches for it.