How to Connect Shopify to Xero (Automated Data Sync)
📊 Integration Overview
This programmatic pipeline establishes a secure, real-time sync between Shopify transaction events and Xero accounting invoices. Upon payment confirmation, Shopify triggers webhook payloads that map customer details, line items, taxes, and payment status to Xero's Invoice and Contact endpoints. This integration minimizes administrative overhead, prevents double-entry errors, and accelerates end-of-month reconciliations.
If you are using WooCommerce instead of Shopify, refer to our WooCommerce to Xero Integration blueprint. For Shopify to QuickBooks setups, review our Shopify to QuickBooks guide.
🛠️ Core Connection Requirements
Primary Key: shopify_order_id map-aligned to Xero InvoiceNumber or a custom tracking field.
Trigger Event: Shopify webhook notification event orders/paid or orders/create (JSON format).
Action Event: Xero Accounting API endpoint operation POST to /api.xro/2.0/Invoices.
📋 The 5-Step Execution Blueprint
Step 1: Authentication & Scope Configuration
Configure secure API credentials for both platforms:
- Shopify: Navigate to Shopify Partners ➔ Apps ➔ Custom Apps. Create an app to get an Admin API Access Token with
read_ordersscopes. - Xero: Create an application on the Xero Developer Portal to get a Client ID and Client Secret. Ensure your app requests
offline_access,accounting.transactions, andaccounting.contactsscopes.
Store credentials in your secure .env configuration file:
SHOPIFY_STORE_URI="cyberdyne-defense.myshopify.com"
SHOPIFY_API_ACCESS_TOKEN="shpat_..."
SHOPIFY_API_CLIENT_SECRET="shpss_..."
XERO_CLIENT_ID="clientXero123"
XERO_CLIENT_SECRET="secretXero456"
XERO_TENANT_ID="tenant-uuid-789"
Step 2: Webhook Trigger Setup
Configure Shopify webhooks under Settings ➔ Notifications ➔ Webhooks. Set the event delivery to orders/paid or orders/create. Register your HTTPS receiver endpoint.
Verify incoming payloads cryptographically using the Shopify webhook secret signature (x-shopify-hmac-sha256 header):
import crypto from 'crypto';
export async function POST(req: Request) {
const rawBody = await req.text();
const signature = req.headers.get('x-shopify-hmac-sha256');
const genHash = crypto
.createHmac('sha256', process.env.SHOPIFY_API_CLIENT_SECRET!)
.update(rawBody, 'utf8')
.digest('base64');
if (genHash !== signature) {
return new Response('Unauthorized Webhook Origin', { status: 401 });
}
// Queue Shopify payload for async transformation
return new Response('OK', { status: 200 });
}
Step 3: Payload Transformation & Mapping
Map the Shopify input order object fields directly into Xero Invoice items and Customer references:
{
"Shopify_Input": {
"id": "Shopify-Order-100293",
"name": "#1029",
"total_price": "249.50",
"subtotal_price": "230.00",
"total_tax": "19.50",
"currency": "USD",
"customer": {
"id": "cust-8821",
"first_name": "Sarah",
"last_name": "Connor",
"email": "sarah@cyberdyne.io"
},
"line_items": [
{
"id": "line-001",
"title": "Quantum Firewall Core",
"quantity": 1,
"price": "230.00",
"sku": "QTY-FW-001"
}
]
},
"Xero_Output": {
"Type": "ACCREC",
"Contact": {
"Name": "Sarah Connor",
"EmailAddress": "sarah@cyberdyne.io"
},
"Date": "2026-06-03",
"DueDate": "2026-06-10",
"InvoiceNumber": "SH-1029",
"LineItems": [
{
"Description": "Quantum Firewall Core (SKU: QTY-FW-001)",
"Quantity": 1.0,
"UnitAmount": 230.00,
"AccountCode": "200",
"TaxAmount": 19.50
}
],
"Status": "AUTHORISED"
}
}
Step 4: Endpoint Despatch & Error Guarding
Post the transformed JSON structure to the Xero API endpoint:
POST https://api.xero.com/api.xro/2.0/Invoices
Handle common errors gracefully within your gateway pipeline:
- 401 Unauthorized: OAuth2 access token expired. Trigger a programmatic token refresh using the Xero identity URL (
https://identity.xero.com/connect/token) with your client secret and refresh token, then retry. - 400 Bad Request: Inspect model fields. Verify Xero
AccountCodereferences (defaulting to sales code 200). - 429 Rate Limit: Xero enforces 60-call-per-minute limits. Queue actions in BullMQ or Redis, throttling processing to a safe interval of 1.1 seconds per API call.
Step 5: Live Loop Validation
Test your end-to-end integration mapping:
- Log into your Shopify Partner store and trigger a dummy checkout event.
- Monitor server logs to confirm signature validation matches.
- Call the Xero Sandbox database GET method
GET /api.xro/2.0/Invoices/SH-1029to verify items, taxes, and client contact values have written correctly.
❓ Integration Frequently Asked Questions
Q: How does this pipeline handle duplicate data entries?
A: Invoices are created in Xero using the unique Shopify Order name appended as InvoiceNumber (e.g., SH-1029). Before committing the payload, the middleware executes a GET check: GET /api.xro/2.0/Invoices?where=InvoiceNumber=="SH-1029". If the invoice exists, the application safely ignores the write or calls a PUT request to update, blocking redundant invoices.
Q: What happens if the API rate limit is exceeded during high volume? A: Webhooks are answered instantly with a 200 status back to Shopify. High-volume order queues are queued using BullMQ inside a background worker. Outbound requests to Xero are throttled to ensure they stay under Xero's limit of 60 requests per minute.