Create Xero sales invoices from new Shopify orders

By General Input

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.

Integrations

  • Shopify
  • Xero
  • Slack Bot

Type

Deterministic Code

Categories

  • Finance
  • Operations

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.

Related prompts

Explore more prompts
Call overdue Xero customers with an AI collections agentA brand asset library your marketing team actually searchesTurn Mailjet email clicks into ranked HubSpot follow-upsClean out the Looker dashboards and Looks nobody opensLiveKit live operations console for room moderationWake up dormant Keap leads with a researched reasonLiveChat coverage board for planning next week's shiftsPhone routing control panel for LiveKit voice agentsLinkedIn Ads budget pacing dashboard for every client accountGive your team Looker numbers without buying more seats