Xero Accounting API – Invoices.
Types: ACCREC (sales invoices to customers) | ACCPAY (bills from suppliers)
Status Flow
DRAFT → SUBMITTED → AUTHORISED → PAID
↓
VOIDEDKey Xero Rules
- Multiple-invoice GET returns contact summary only (no line items).
Use
page:parameter to get full line items in bulk. - Maximum 50 invoices per PUT (counts as 1 API call).
summary_only: trueskips computation-heavy fields for fast responses.if_modified_since:enables efficient incremental sync.- Requests requiring processing of >100k invoices are rejected (HTTP 400).
Use filters, paging, and
summary_onlyto reduce working set size.
Examples
# List AUTHORISED invoices (page 1, with full line items)
{:ok, result} = Xero.Accounting.Invoices.list(token, tenant_id,
statuses: ["AUTHORISED"], page: 1)
# Incremental sync: only changed since last run
{:ok, result} = Xero.Accounting.Invoices.list(token, tenant_id,
if_modified_since: last_sync_datetime)
# Get a single invoice (full line items always included)
{:ok, %{"Invoices" => [inv]}} = Xero.Accounting.Invoices.get(token, tenant_id, invoice_id)
# Get as PDF
{:ok, %{body: pdf_bytes}} = Xero.Accounting.Invoices.get(token, tenant_id, id, format: :pdf)
# Create invoices (up to 50 at once)
{:ok, _} = Xero.Accounting.Invoices.create(token, tenant_id, %{
"Type" => "ACCREC",
"Contact" => %{"ContactID" => "uuid"},
"LineItems" => [%{
"Description" => "Consulting",
"Quantity" => 1.0,
"UnitAmount" => 500.0,
"AccountCode" => "200"
}],
"Status" => "AUTHORISED"
})
# Stream all PAID invoices lazily
Xero.Accounting.Invoices.stream(token, tenant_id, statuses: ["PAID"])
|> Enum.each(&process/1)
Summary
Functions
Adds a note to an invoice's history.
Lists attachments on an invoice.
Creates one or more invoices (up to 50 per request, counts as 1 API call).
Deletes a DRAFT or SUBMITTED invoice.
Emails an ACCREC invoice to the contact's primary email address.
Retrieves a single invoice by ID or invoice number.
Gets a specific attachment by filename.
Returns the audit history for an invoice.
Lists invoices. Returns summary-only data for multiple invoices unless
:page is specified (paging returns full line item details).
Returns the online payment URL for an AUTHORISED ACCREC invoice.
Returns a lazy Stream of all invoices, auto-paginating through results.
Updates an existing invoice.
Uploads an attachment to an invoice.
Voids an AUTHORISED invoice.
Functions
@spec add_note(Xero.Auth.Token.t(), String.t(), String.t(), String.t()) :: {:ok, map()} | {:error, Xero.Error.t()}
Adds a note to an invoice's history.
@spec attachments(Xero.Auth.Token.t(), String.t(), String.t()) :: {:ok, map()} | {:error, Xero.Error.t()}
Lists attachments on an invoice.
@spec create(Xero.Auth.Token.t(), String.t(), map() | [map()], keyword()) :: {:ok, map()} | {:error, Xero.Error.t()}
Creates one or more invoices (up to 50 per request, counts as 1 API call).
Pass a single map for one invoice, or a list of maps for bulk creation. Uses PUT (Xero's convention for creates with list response).
Required fields
"Type"—"ACCREC"or"ACCPAY""Contact"—%{"ContactID" => "uuid"}or%{"Name" => "Supplier Ltd"}"LineItems"— List of line item maps
Optional fields
"Date","DueDate"— ISO 8601 date strings"Status"—"DRAFT"(default) |"SUBMITTED"|"AUTHORISED""Reference","CurrencyCode","BrandingThemeID""LineAmountTypes"—"Exclusive"|"Inclusive"|"NoTax""SentToContact"— Mark invoice as sent (boolean)"Url"— Link to an external resource
@spec delete(Xero.Auth.Token.t(), String.t(), String.t()) :: {:ok, map()} | {:error, Xero.Error.t()}
Deletes a DRAFT or SUBMITTED invoice.
@spec email(Xero.Auth.Token.t(), String.t(), String.t()) :: :ok | {:error, Xero.Error.t()}
Emails an ACCREC invoice to the contact's primary email address.
Invoice must be type ACCREC and status SUBMITTED, AUTHORISED, or PAID.
Also sends to contact persons with IncludeInEmails: true.
@spec get(Xero.Auth.Token.t(), String.t(), String.t(), keyword()) :: {:ok, map()} | {:error, Xero.Error.t()}
Retrieves a single invoice by ID or invoice number.
Always returns full line item details.
Options
:format—:pdfto receive the invoice as a PDF binary:unit_dp— Decimal places for unit amounts (4 default)
@spec get_attachment(Xero.Auth.Token.t(), String.t(), String.t(), String.t()) :: {:ok, binary()} | {:error, Xero.Error.t()}
Gets a specific attachment by filename.
@spec history(Xero.Auth.Token.t(), String.t(), String.t()) :: {:ok, map()} | {:error, Xero.Error.t()}
Returns the audit history for an invoice.
@spec list(Xero.Auth.Token.t(), String.t(), keyword()) :: {:ok, map()} | {:error, Xero.Error.t()}
Lists invoices. Returns summary-only data for multiple invoices unless
:page is specified (paging returns full line item details).
Options
:page— Page number (1-indexed, 100/page). Enables full line item details.:statuses— List of statuses:["DRAFT", "AUTHORISED", "PAID", ...]:contact_ids— List of contact UUIDs:invoice_numbers— List of invoice numbers:ids— List of invoice UUIDs:reference— Reference field exact match:summary_only— Return lightweight response (boolean):order— Order by field, e.g."Date DESC","UpdatedDateUTC ASC":where— OData filter:~s(AmountDue > 0 AND Status == "AUTHORISED"):created_by_my_app— Only return invoices created by this app (boolean):if_modified_since—DateTime.t()— only return records modified after this time:unit_dp— Number of decimal places for unit amounts (4 default)
@spec online_url(Xero.Auth.Token.t(), String.t(), String.t()) :: {:ok, String.t()} | {:error, Xero.Error.t()}
Returns the online payment URL for an AUTHORISED ACCREC invoice.
@spec stream(Xero.Auth.Token.t(), String.t(), keyword()) :: Enumerable.t()
Returns a lazy Stream of all invoices, auto-paginating through results.
Accepts all options from list/3 except :page.
Example
Xero.Accounting.Invoices.stream(token, tenant_id,
statuses: ["AUTHORISED", "PAID"],
if_modified_since: last_sync)
|> Stream.filter(&(&1["AmountDue"] > 0))
|> Enum.each(&sync_invoice/1)
@spec update(Xero.Auth.Token.t(), String.t(), String.t(), map()) :: {:ok, map()} | {:error, Xero.Error.t()}
Updates an existing invoice.
Only DRAFT and SUBMITTED invoices can be fully updated.
AUTHORISED invoices: only Reference, SentToContact, Url, and Status can change.
@spec upload_attachment( Xero.Auth.Token.t(), String.t(), String.t(), String.t(), binary(), String.t() ) :: {:ok, map()} | {:error, Xero.Error.t()}
Uploads an attachment to an invoice.
@spec void(Xero.Auth.Token.t(), String.t(), String.t()) :: {:ok, map()} | {:error, Xero.Error.t()}
Voids an AUTHORISED invoice.