AccountingKitDocs

Provider-native Xero API

Lossless Xero operations for accounting workflows that exceed the portable model.

Why this API exists

The normalized resource API covers portable accounting fields. Production quote-to-cash and procure-to-pay workflows also depend on provider-native fields such as Xero ContactPersons, Terms, BrandingThemeID, LineAmountTypes, delivery instructions, and status transition rules. AccountingKit therefore exposes a typed transport boundary without discarding the original Xero document.

Native operations use the same API key, connection authorization, rate budget, audit log, idempotency, encrypted raw cache, and provider error handling as normalized operations.

One active AccountingKit connection owns each Xero tenant. This is required because Xero sends app-wide webhook events with a tenant ID but no AccountingKit environment identifier; ambiguous tenant ownership is rejected during connection rather than routed by recency.

Live provider lookup

Fetch and cache a resource when the caller already holds its immutable Xero ID:

GET /v1/connections/{connection_id}/invoices/by-provider-id/{xero_invoice_id}

Search Xero live with constrained exact filters:

POST /v1/connections/{connection_id}/quotes/search
Content-Type: application/json

{"number":"QU-1042"}

Supported filters are provider_id, external_reference, number, name, status, and page. Arbitrary Xero where clauses are intentionally not exposed.

Lossless writes

Send a single native Xero resource inside provider_data. The standard Idempotency-Key header is required.

Quote, invoice, purchase-order, and payment creates use the idempotency key, not Reference or external_reference, to identify a retry. References may repeat across customers and documents; a different create key does not reuse an existing document merely because its reference matches. Retry an uncertain create with the same key and unchanged payload, never a newly generated key. Contacts and accounts retain reconciliation by their master-data identifiers (ContactNumber and account Code).

POST /v1/connections/{connection_id}/invoices
Idempotency-Key: order-10042
Content-Type: application/json

{
  "provider_data": {
    "Type": "ACCREC",
    "Contact": {"ContactID": "..."},
    "Reference": "ORDER-10042",
    "LineAmountTypes": "Inclusive",
    "LineItems": [],
    "Status": "AUTHORISED"
  }
}

For a status or partial update where omitted Xero fields must be retained, set preserve_existing: true. AccountingKit fetches the live provider record, overlays the supplied fields, and writes the merged document.

For quotes, Xero can return tax-exclusive line amounts under an Inclusive header. When retaining those lines, AccountingKit normalizes the outgoing mode to Exclusive to preserve the monetary totals, including on status-only updates. Explicit replacement LineItems are sent as supplied. If inherited inclusive lines cannot be reconciled with the live quote's subtotal or total, the update fails with HTTP 409 (CONFLICT, "Cannot safely preserve Xero quote line amounts") before writing.

Accounts use Xero's account-specific transport (PUT /Accounts to create and POST /Accounts/{AccountID} to update). Contacts, invoices, quotes, payments, purchase orders, and accounts accept provider-native creates where Xero supports them. Organizations, tax rates, and branding themes are explicitly read-only in v1.

Quote search uses Xero's native QuoteNumber and Status parameters and then applies an exact client-side match because Xero's quote-number filter is partial. Quote conversion follows Xero's state machine (DRAFT → SENT → ACCEPTED → INVOICED) and strips quote-owned line IDs and calculated amounts before creating the invoice.

Rendered documents

The provider actions support Xero-rendered invoice, quote, and purchase-order PDFs:

  • invoice.download_pdf
  • quote.download_pdf
  • purchase_order.download_pdf

Pass either AccountingKit's resource_id or Xero's provider_id. Invoice online URLs and organization deep links remain separate provider actions.

Payment lifecycle

Xero invoice webhooks trigger a targeted live read. AccountingKit maintains a dedicated payment projection per connection and invoice, so unrelated reads cannot erase the prior webhook state. The durable operation emits:

  • invoice.updated for every accepted invoice event;
  • invoice.payment_changed when status, amount paid, or amount due changes;
  • invoice.paid only on a transition from a non-paid state to paid.

The transition snapshot is persisted as an operation step before delivery. If delivery dispatch fails and the operation retries, the original transition is replayed instead of being lost.

Invoice cancellation

Cancel an invoice through the dedicated idempotent action:

POST /v1/connections/{connection_id}/invoices/{resource_id}/cancel
Idempotency-Key: cancel-order-10042
Content-Type: application/json

{"reason":"Customer cancelled before onboarding"}

When the caller already stores Xero's immutable invoice ID, use POST /v1/connections/{connection_id}/invoices/by-provider-id/{xero_invoice_id}/cancel.

AccountingKit reads the current invoice from Xero before choosing the transition. DRAFT and SUBMITTED invoices become DELETED; unpaid AUTHORISED invoices become VOIDED; already deleted or voided invoices succeed idempotently. Paid, part-paid, credited, and unknown states are rejected instead of guessing or issuing an invalid Xero mutation.

On this page