Finance & Ops · n8n

Automatically Post Zendrop Invoices as Xero Expenses — Zero Manual Entry

This recipe pulls new invoices from Zendrop via its API and creates matching expense transactions in Xero automatically. Accountants save hours each week by eliminating copy-paste data entry between the two platforms.

difficulty Intermediatesetup 45 minresult Every new Zendrop invoice is posted as a bills/expense record in Xero within minutes, fully categorised and ready for approval.
  1. 1

    Schedule the workflow to run hourly

    Add a Schedule Trigger node. Set Rule to Every Hour. This polls Zendrop regularly so no invoice is missed. You can tighten to every 30 minutes during busy periods.

  2. 2

    Fetch new invoices from Zendrop

    Add an HTTP Request node named Get Zendrop Invoices. Set Method to GET and URL to https://api.zendrop.com/v1/invoices. Add a Header called Authorization with value Bearer YOUR_ZENDROP_API_KEY. In the node Notes, paste your API key reminder. Set Query Parameter status to unpaid to fetch only new invoices.

  3. 3

    Extract and map invoice fields

    Add a Set node named Map Invoice Fields. Create fields: invoiceNumber mapped to {{ $json.invoice_number }}, amount mapped to {{ $json.total_amount }}, currency mapped to {{ $json.currency }}, invoiceDate mapped to {{ $json.created_at }}, and description mapped to Zendrop Order - {{ $json.order_id }}. These become the payload sent to Xero.

  4. 4

    Create the expense bill in Xero

    Add an HTTP Request node named Create Xero Bill. Set Method to POST and URL to https://api.xero.com/api.xro/2.0/Invoices. Under Authentication choose OAuth2 and connect your Xero OAuth2 credential (Client ID and Secret from Xero Developer portal). Set Body Content Type to JSON and paste the body: { "Type": "ACCPAY", "InvoiceNumber": "{{ $json.invoiceNumber }}", "Date": "{{ $json.invoiceDate }}", "DueDate": "{{ $json.invoiceDate }}", "CurrencyCode": "{{ $json.currency }}", "LineItems": [{ "Description": "{{ $json.description }}", "Quantity": 1, "UnitAmount": {{ $json.amount }}, "AccountCode": "300" }] }. Update AccountCode 300 to match your Xero chart of accounts.

  5. 5

    Handle errors with a conditional check

    Add an If node named Check Xero Response. Set Condition to {{ $json.Status }} equals OK. Route the true branch to end (success). On the false branch you can add a future notification node. This prevents silent failures when Xero rejects a malformed request.

Frequently asked questions

What Xero account code should I use for Zendrop expenses?

Use whichever account code your chart of accounts assigns to cost of goods sold or supplier purchases — commonly 300 or 310 in Xero's default chart. Ask your accountant to confirm the correct code before activating the workflow.

How do I avoid duplicate bills if the workflow runs multiple times?

Zendrop's API returns invoices filtered by status. Once you mark an invoice as paid or processed in Zendrop, it will no longer appear in the `unpaid` query. Alternatively, store processed invoice IDs in a Google Sheet and add an If node to skip already-seen IDs.

Does this workflow support multiple currencies?

Yes. The `Map Invoice Fields` step passes the `currency` field directly from Zendrop to Xero's `CurrencyCode`. Make sure the currency is enabled in your Xero organisation settings under Currencies before the first bill is created.

About this recipe. Recipes on FlowRecipesHub are written for business owners, not developers, and are tested before publishing — how recipes get made. Some ingredient links are affiliate links that cost you nothing — full disclosure.