WooCommerce orders flowing through an automation pipeline into an accounting book

WooCommerce Order Sync to Moneybird via n8n

Stanislav Kapustin May 1, 2026 case study · automation · n8n · woocommerce · moneybird · accounting

Case summary

Quick scan before the full breakdown.

Goal

Eliminate weekly CSV exports and manual Moneybird invoice creation for a Dutch WooCommerce webshop.

Stack

n8n, WooCommerce Webhooks, Moneybird API, Google Sheets, Slack

Result

100% of orders synced to Moneybird automatically with zero VAT errors across 10 weeks.

Time saved

Removed about 2 hours per week of bookkeeper data entry.

An n8n workflow that picks up every new WooCommerce order the moment it’s paid, creates the contact and invoice in Moneybird automatically, marks it as sent, and handles refunds and partial payments without a human touching the accounting software.


The Problem

A Dutch webshop selling physical products — around 120 orders per month — was running WooCommerce on a self-hosted WordPress installation. Their bookkeeper used Moneybird for all accounting. The sync between the two didn’t exist.

Every Monday morning the owner exported a CSV from WooCommerce and handed it to the bookkeeper, who manually created invoices in Moneybird, matched them to the correct contacts, applied the right VAT rates (21% for most products, 9% for a few categories), and marked paid ones as received. The process took the bookkeeper about two hours per week.

Refunds were worse — WooCommerce generated a credit note that had to be manually matched and processed separately.

The question was: why is anyone doing this by hand?


What I Built

Two n8n workflows running on a self-hosted VPS.

Workflow 1 — Order to Invoice: Triggered by a WooCommerce webhook on order.payment_complete. The webhook fires the moment a payment is confirmed — iDEAL, credit card, or manual bank transfer.

Steps inside the workflow:

  1. Parse the webhook payload — extract customer details, order lines, totals, VAT breakdown, and WooCommerce order ID
  2. Check if the contact already exists in Moneybird by querying /contacts with the customer email. If yes, use the existing contact ID. If no, create a new contact with name, email, address, and (if provided) KVK number
  3. Build the Moneybird invoice payload — one detail line per WooCommerce order line, with the correct tax_rate_id mapped from the WooCommerce VAT rate
  4. POST to /sales_invoices to create the invoice in draft state
  5. POST to /sales_invoices/{id}/send_invoice to mark it as sent (Moneybird requires this to register it as an outstanding receivable)
  6. POST to /sales_invoices/{id}/register_payment with the payment date and amount from the WooCommerce order — marks it as paid immediately for orders where payment was confirmed at checkout
  7. Store the Moneybird invoice ID against the WooCommerce order ID in a small Google Sheet lookup table (used by Workflow 2)

Workflow 2 — Refund to Credit Note: Triggered by a WooCommerce webhook on order.refunded. Looks up the Moneybird invoice ID from the lookup table, creates a credit note via /credit_notes, and links it to the original invoice.


The VAT Mapping Problem

WooCommerce stores VAT as a percentage. Moneybird requires a tax_rate_id — a numeric ID specific to your Moneybird administration. These don’t map automatically.

I built a static mapping object in the workflow:

{
  "21.00": "{{MONEYBIRD_TAX_ID_HIGH}}",
  "9.00": "{{MONEYBIRD_TAX_ID_LOW}}",
  "0.00": "{{MONEYBIRD_TAX_ID_ZERO}}"
}

The actual IDs come from a one-time GET to /tax_rates during setup and are stored as n8n credentials. Any VAT rate that doesn’t match sends a Slack alert so it doesn’t silently fall through.


Edge Cases Handled

  • Guest checkout (no account in WooCommerce): the webhook still contains full billing details, so contact creation works fine
  • Orders with multiple VAT rates (mixed 9% and 21% products): each order line gets its own detail row in Moneybird with the correct rate
  • Partially refunded orders: the refund webhook includes the exact line items and amounts being refunded, so the credit note reflects only what was actually returned
  • Duplicate protection: before creating a contact, the workflow checks email. Before creating an invoice, it checks the Google Sheet for an existing Moneybird ID for that WooCommerce order ID. Duplicates are skipped with a Slack notification

Results

Running for 10 weeks with an average of 127 orders and 11 refunds per month:

  • 100% of orders synced to Moneybird without manual intervention
  • Bookkeeper time saved: approximately 2 hours per week — fully eliminated from the accounting workflow
  • Sync delay: average 8 seconds from WooCommerce payment confirmation to Moneybird invoice creation
  • Zero VAT errors in 10 weeks — previously the main source of manual corrections
  • The Monday morning CSV export ritual no longer exists

The bookkeeper now opens Moneybird to find everything already processed. Their work on this client shifted from data entry to actual bookkeeping — reviewing the period, checking outstanding balances, preparing BTW returns.


What I’d Do Differently

The Google Sheet lookup table works but is fragile — if the sheet is unavailable, Workflow 2 fails silently. A better approach is storing the WooCommerce-to-Moneybird ID mapping directly in the WooCommerce order as custom metadata via the WooCommerce REST API. Fewer dependencies, cleaner audit trail.


Stack

  • n8n (self-hosted)
  • WooCommerce Webhooks — order and refund triggers
  • Moneybird REST API — contacts, invoices, credit notes, payments
  • Google Sheets — order ID lookup table
  • Slack — exception alerts

Want to connect your WooCommerce store to Moneybird or another Dutch accounting system? Get in touch.

More cases

Three nearby case studies worth reading next.

Need a similar system in your business?

If you have a manual workflow between tools, I can help map the logic, design the system, and automate it in a way your team can actually use.

svg