# Create Xero sales invoices from new Shopify orders

> Every new Shopify order becomes a Xero contact and sales invoice with the right line items and tax, plus a Slack receipt for your finance channel.

- Workflow type: code
- Services: Shopify, Xero, Slack Bot
- Categories: Finance, Operations
- Published: 2026-07-12

## What it does

- Listens for new Shopify orders and, for each one, matches or creates the buyer as a Xero contact by email.
- Creates a matching Xero sales invoice with one line per Shopify item, including quantity, unit price, and the tax Shopify already calculated.
- Skips cancelled and refunded orders so your Xero ledger only reflects real revenue.
- Posts a short receipt in Slack with the invoice number and total so finance sees every sale as it lands.

## What you'll need

- A Shopify store you can connect and permission to receive order webhooks.
- A Xero organisation you can connect, with the revenue account code you use for online sales.
- A Slack workspace and the channel you want the receipts posted in.
- A decision on whether new invoices should land as drafts for finance to approve, or be authorised and ready to send straight away.

## Prompt

Whenever a new order is created in Shopify, run this workflow. Subscribe to the Shopify orders/create outgoing webhook so we fire the moment a new order lands, and use the webhook payload as the source of truth for the rest of the run. This is a deterministic code workflow: one Shopify order in, one Xero contact upsert, one Xero sales invoice, one Slack receipt out, in that fixed order.

Expose two customer-set variables at the top of the workflow so shops can wire this to their own accounting setup without editing code:

1. invoiceStatus — one of DRAFT or AUTHORISED. Default to DRAFT. This is passed straight through to Xero as the Status field on Create Invoices, so shops that want finance to review each invoice keep DRAFT, and shops that want to bill immediately set AUTHORISED.

2. revenueAccountCode — the Xero revenue account code (from the shop's chart of accounts) that every line item on the invoice should post to. No default; require the shop to set it during setup.

Also expose slackChannel as a customer-set variable — the Slack channel ID or name where the receipt should be posted.

Step 1: filter out orders we should not invoice. If the incoming Shopify order has cancelled_at set, or its financial_status is refunded or partially_refunded, or its status is cancelled, exit early without creating anything. Log the skip reason and do not post to Slack — this workflow is only about creating invoices for real, live orders.

Step 2: upsert the buyer as a Xero contact using Xero's Update or Create Contacts operation, keyed by email. Take email, first name, last name, phone, and billing address from the Shopify customer and billing_address on the order. If the Shopify customer has no email (guest checkout without an email), fall back to a synthesized placeholder like shopify-order-{order_id}@no-email.local and note the fallback in the invoice reference so finance can spot it. Update or Create Contacts is an upsert — pass the email as the match key so returning buyers reuse the existing Xero contact instead of creating duplicates.

Step 3: create the sales invoice in Xero using Create Invoices. Set Type to ACCREC (accounts receivable / sales invoice). Set Contact to the ContactID returned in step 2. Set Status to the invoiceStatus variable. Set Reference to the Shopify order name (e.g. #1042) so finance can match invoices back to Shopify at a glance. Use the Shopify order's created_at as the invoice Date, and default DueDate to Date + 0 (invoice due on issue) unless the shop overrides via a dueDateOffsetDays variable.

For LineItems, emit one Xero line per Shopify line_items entry. For each Shopify line: Description = title (append variant_title in parentheses if present), Quantity = quantity, UnitAmount = price (from the Shopify line, in shop currency — do not use presentment_currency, because the shop's Xero base currency is the shop currency and Xero handles FX on the payment side), AccountCode = revenueAccountCode. Pass the tax through by setting LineAmountTypes on the invoice to Exclusive and populating TaxAmount on each line from the Shopify line's tax_lines sum, so the invoice total matches what the customer actually paid. If a Shopify line has no tax_lines, omit TaxAmount and let Xero apply the default tax rate for the account code.

Also add discount and shipping as separate lines when present: if the order has a total_discounts value greater than zero, add a negative-amount line for the discount pointing at the same revenue account code. If shipping_lines is non-empty, add a shipping line (Description = the Shopify shipping title, UnitAmount = shipping price, TaxAmount = shipping tax) — for this line the account code should be the shop's shipping revenue code if the shop has set one via a shippingAccountCode variable, otherwise reuse revenueAccountCode.

Step 4: post a short receipt to Slack via the slackbot Send a Message operation, into slackChannel. Format it as a one-liner: 'Xero invoice {InvoiceNumber} created for {contact name} — {total in shop currency}'. Include a link to the invoice in Xero if the Create Invoices response contains one. Keep it terse — this is a finance-channel receipt, not a marketing message.

Error handling: if Xero returns a validation error on Create Invoices (for example, an invalid AccountCode), post a Slack error message into slackChannel with the Shopify order number and the Xero error text, and do not silently swallow. If the Xero contact upsert succeeds but invoice creation fails, log both so a human can retry. Never create the invoice if the contact upsert failed. Do not retry inside this workflow on 4xx errors from Xero — those need human attention.

Do not add payments, do not touch Xero's bank feed, and do not reconcile against Stripe or Shopify Payments in this workflow — payment reconciliation is a separate concern. This workflow's job is: order in, invoice out, receipt posted.

## How to customize

- Set the initial invoice status to Draft if finance reviews every sale, or Authorised if you want invoices ready to bill immediately.
- Pick the Xero revenue account code that matches your chart of accounts (for example, one code for online sales, another for wholesale).
- Choose the Slack channel where the finance receipt lands, and reword the message if you want more detail than invoice number and total.

## FAQ

### Will this work with Shopify Basic?

Yes. Any Shopify plan that supports outgoing order webhooks works, including Basic. The workflow subscribes to the standard order-created event, so you do not need Shopify Plus.

### Do I have to bill customers straight away?

No. You choose the initial invoice status when you set up the workflow. Set it to Draft if your finance team wants to review every invoice before it goes out, or Authorised if you want invoices ready to send the moment an order comes in.

### What happens to cancelled or refunded orders?

They are skipped. The workflow only creates a Xero invoice for orders that are live and paid or pending, so your ledger stays clean and you do not have to void invoices after the fact.

### How is tax handled?

The workflow reuses the tax breakdown Shopify already calculated on the order, so the invoice matches what the customer actually paid. You do not have to rebuild tax rules inside Xero.

### Which revenue account does the invoice post to?

Whichever revenue account code from your Xero chart of accounts you set up front. That way sales from this workflow land in the right place for reporting, and you can point wholesale versus retail stores at different codes.

### Can I send the receipt somewhere other than Slack?

The default is a Slack channel because that is where most finance teams already live. You can point it at any channel you have access to, and adjust the message wording during setup.

Use this prompt in General Input: https://www.generalinput.com/prompts/create-xero-sales-invoices-from-new-shopify-orders