How to Connect WooCommerce to Xero (Automated Data Sync)
📊 Integration Overview
This programmatic pipeline establishes a secure, real-time sync between WooCommerce order completions and Xero accounting invoices. Upon payment confirmation, WooCommerce 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.
For other e-commerce accounting workflows, you can also explore the Shopify to QuickBooks blueprint.
🛠️ Core Connection Requirements
Primary Key: woocommerce_order_id map-aligned to Xero InvoiceNumber or a custom tracking field.
Trigger Event: WooCommerce webhook notification event action.woocommerce_payment_complete or order.created (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:
- WooCommerce: Navigate to WooCommerce ➔ Settings ➔ Advanced ➔ REST API. Create a Read/Write API key to obtain a Consumer Key (
ck_...) and Consumer Secret (cs_...). - 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:
WOOCOMMERCE_STORE_URL="https://yourstore.com"
WOOCOMMERCE_CONSUMER_KEY="ck_..."
WOOCOMMERCE_CONSUMER_SECRET="cs_..."
XERO_CLIENT_ID="clientXero123"
XERO_CLIENT_SECRET="secretXero456"
XERO_TENANT_ID="tenant-uuid-789"
Step 2: Webhook Trigger Setup
Configure WooCommerce webhooks under WooCommerce ➔ Settings ➔ Advanced ➔ Webhooks. Set the event delivery to order.updated or action.woocommerce_payment_complete. Register your HTTPS receiver endpoint.
Verify incoming payloads cryptographically using the WooCommerce webhook secret signature (x-wc-webhook-signature header):
import crypto from 'crypto';
export async function POST(req: Request) {
const rawBody = await req.text();
const signature = req.headers.get('x-wc-webhook-signature');
const secret = process.env.WOOCOMMERCE_WEBHOOK_SECRET!;
const hash = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
if (hash !== signature) {
return new Response('Unauthorized Webhook Origin', { status: 401 });
}
// Queue WooCommerce payload for async transformation
return new Response('OK', { status: 200 });
}
Step 3: Payload Transformation & Mapping
Map the WooCommerce input order object fields directly into Xero Invoice items and Customer references:
{
"WooCommerce_Input": {
"id": 8573,
"order_key": "wc_order_abc123",
"status": "processing",
"total": "189.00",
"total_tax": "15.00",
"currency": "USD",
"billing": {
"first_name": "Bruce",
"last_name": "Wayne",
"email": "bruce@waynecorp.com",
"company": "Wayne Enterprises"
},
"line_items": [
{
"id": 12,
"name": "Tactical Batsuit Utility Belt",
"product_id": 99,
"quantity": 1,
"subtotal": "174.00",
"price": 174.00,
"sku": "BAT-BELT-007"
}
]
},
"Xero_Output": {
"Type": "ACCREC",
"Contact": {
"Name": "Bruce Wayne",
"EmailAddress": "bruce@waynecorp.com"
},
"Date": "2026-06-03",
"DueDate": "2026-06-10",
"InvoiceNumber": "WC-8573",
"LineItems": [
{
"Description": "Tactical Batsuit Utility Belt (SKU: BAT-BELT-007)",
"Quantity": 1.0,
"UnitAmount": 174.00,
"AccountCode": "200",
"TaxAmount": 15.00
}
],
"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 WooCommerce developer 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/WC-8573to 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 WooCommerce Order ID appended as InvoiceNumber (e.g., WC-8573). Before committing the payload, the middleware executes a GET check: GET /api.xro/2.0/Invoices?where=InvoiceNumber=="WC-8573". 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 WooCommerce. 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.