← all adapters

xero-style

simulates Xero Accounting API 2.0

restadapter v0.1.015 routes

verification

VMVM tier — no SDK suite yet· surface 15 · not implemented 6 · differs 5

use it

# add to stunt.yaml
services:
  xero:
    adapter: embedded:xero-style

# then
$ stunt up

The embedded: source extracts this adapter from the binary — no git clone, fully offline.

how to mock the Xero Accounting API locally

Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of Xero Accounting API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real Xero Accounting API 2.0 surface — the data is fake, the behavior is real.

backing stores

contactsinvoicesaccounts

api surface (15)

generated from the adapter manifest — exactly what boots, nothing more · covers 4% of the real API's 235 routes (derived from spec Xero-OpenAPI xero_accounting @ 17.0.0)

15 routes
GET/connections
GET/api.xro/2.0/Contacts
PUT/api.xro/2.0/Contacts
GET/api.xro/2.0/Contacts/{id}
PUT/api.xro/2.0/Contacts/{id}
GET/api.xro/2.0/Invoices
PUT/api.xro/2.0/Invoices
GET/api.xro/2.0/Invoices/{id}
DELETE/api.xro/2.0/Invoices/{id}
POST/api.xro/2.0/Invoices/{id}/Payments
GET/api.xro/2.0/Accounts
GET/api.xro/2.0/BankTransactions
GET/api.xro/2.0/Items
GET/api.xro/2.0/TrackingCategories
POST/webhooks

VM-verified behaviors (29)

  • a missing or unknown bearer gets the same 401 TokenExpired envelope; the seeded static token passes
  • GET /connections needs only the bearer and lists the two demo tenants
  • api.xro calls without xero-tenant-id are 400 TenantRequired, and the header matches case-insensitively
  • the seeded static token expires on the virtual clock, and the bearer gate outranks the tenant gate
  • PUT /Invoices sums every line into the totals and echoes each computed LineAmount
  • a single-object body creates one invoice with clock-derived Date and DueDate defaults
  • GET /Invoices/{id} round-trips the stored invoice; unknown ids are the 404 envelope
  • payments accumulate on the ledger and are echoed with their own PaymentID
  • a negative amount is a refund bounded by what was paid
  • over-payment (and an over-refund) is Xero's validation error and leaves the ledger untouched
  • the invoice flips to PAID exactly at a zero balance and then refuses further payments
  • DRAFT invoices are not payable and unknown ids are the 404 envelope
  • DELETE voids to 204 and the record survives with Status VOIDED and a zeroed balance
  • the voided invoice stays listable via the Statuses filter
  • re-voiding, voiding a PAID invoice, and unknown ids are rejected
  • PUT /Contacts creates with Xero defaults and reads back by id
  • the Contacts array upserts by ContactID and ContactNumber, merging over the stored record
  • duplicate active names are Xero's real validation error, in Elements
  • archive is an update: the contact stays readable and releases its name
  • invalid statuses and unknown ids are the plain ValidationError / NotFound envelopes
  • where ANDs clauses in Xero's grammar, and Statuses/InvoiceNumber/ContactID filter the invoice list
  • order sorts by any field in both directions (dates sort as instants)
  • contacts search matches name and email case-blind, while where Name.Contains is case-sensitive
  • the seeded chart of accounts and the static catalogs speak the same grammar
  • page/pageSize walk every contact exactly once, with nextPage only between pages
  • without pageSize paging is off; a page past the end is an empty 200 page
  • a malformed page falls back to page 1 (as-is: no 400)
  • a delivery signed with base64(HMAC-SHA256(key, raw bytes)) is accepted
  • missing, tampered, and wrong-key signatures all answer the 401 envelope Xero requires

Not implemented (6)

  • No Credit Notes, Quotes, or standalone Payments endpoints
  • No Journals, Bank Transfers, Employees, or Expense Claims
  • No Organisations, Currencies, or TaxRates endpoints
  • Accounts, Items, TrackingCategories, BankTransactions are read-only (GET only)
  • No attachments or history/notes endpoints
  • No invoice update, email, or repeating-invoice endpoints

Differs from the real API (5)

  • No tax-rate registry; line tax is the given TaxAmount, not computed from TaxType
  • Responses carry a sim-only nextPage field; real Xero signals the last page by item count
  • GET /connections wraps entries in {connections:[...]} with tenantName; real Xero returns a bare array without it
  • Malformed page falls back to page 1; real Xero answers 400
  • nextPage envelope field is a simulator extension; 401 ErrorNumber is the string TokenExpired, not numeric
want more of the Xero Accounting API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.