Olmec Dynamics
H
·8 min read

How to Sync Xero Invoice Events to Notion and Slack Using Xero Webhooks and Make.com

Automatically sync Xero invoice events to Notion and alert Slack using Xero webhooks and Make.com. Keep a single source of truth and reduce manual reconciliation.

Introduction

Chasing invoice updates across systems is slow: someone exports a report from Xero, pastes rows into Notion or Sheets, and then pings finance in Slack when a payment or correction is required. That manual chain creates timing gaps, duplicate records, and no reliable audit trail.

This guide walks through an advanced, production-grade automation: Xero sends invoice events via webhooks, Make.com receives and normalises the payload, the scenario writes or updates a canonical invoice record in Notion, and Slack gets a concise, contextual alert when the invoice needs action. By the end you will have an idempotent, auditable pipeline suitable for finance teams.

What you will know by the end: the exact Make.com modules to use, Notion mapping and rate-limit mitigations, deduplication logic, and Slack formatting for actionable alerts.

What You'll Need

  • Xero account with API access and a webhook subscription for invoice events (INVOICE.CREATE, INVOICE.UPDATE). Xero webhooks require a connected Xero application.
  • Notion workspace with a database set up for invoices and a Notion integration token, plus the database property IDs (Notion enforces per-integration rate limits, see notes below).
  • Make.com account with Xero, Notion, and Slack connections. Paid Make.com plan recommended for production webhook reliability and retries.
  • Slack workspace with a channel to post finance alerts and a Slack app authorised in Make.com.
  • A Google Sheet or Drive folder optional for raw webhook archival if you want a replayable snapshot outside Notion.

Permissions and limits: Xero webhooks will send events but expect occasional retries. Notion rate limits average about 3 requests per second per integration, so design batching or backoff when writing many updates.

How It Works (The Logic)

Trigger (Xero webhook) → Validate and archive raw payload → Deduplicate on event.id or invoice id + event timestamp → Fetch canonical invoice from Xero API if webhook snapshot is partial → Normalize fields (dates, currencies, contact ids) → Upsert invoice record into Notion → Post a Slack message only for actionable changes (status, overdue, payment applied) → Log result.

This pattern keeps Notion as your single source of truth for downstream workflows while using Slack for human triage.

Step-by-Step Setup

  1. Subscribe to Xero invoice webhooks
  • In your Xero developer console create a webhook subscription for invoice events (INVOICE.CREATE, INVOICE.UPDATE). Point it at a secure public URL provided by Make.com (use a webhook module) or your middleware endpoint.
  • Configure a shared secret or validate the origin according to Xero docs. Never treat webhook delivery as guaranteed once-only: Xero can re-send events.

Gotcha: webhooks can omit long-lived related entity data. Plan to call the Xero Invoices GET endpoint when you need a full canonical record.

  1. Create a new Scenario in Make.com with a Webhook trigger
  • Module: Webhooks, "Custom webhook". Paste the webhook URL into Xero.
  • Name the module TRIGGER: Xero Invoice Event and set it to accept POST.
  • For testing, enable sample deliveries and capture a few real webhook payloads so you can inspect the fields Make.com receives.
  1. Immediately archive the raw payload for auditability
  • Add a module to save the raw payload to Google Drive or a Google Sheet row. Include timestamp, Xero event id, and the webhook headers.
  • Use this archive as your replayable raw_snapshot_id in case you need to reprocess or debug later.

Why: raw snapshots give you a replay path without depending on third-party retention policies.

  1. Validate and parse the webhook, then deduplicate
  • Add a Tools: JSON parse module and extract keys: eventId, eventType, resourceId (invoice id), and eventTimestamp.
  • Use a Notion or Google Sheets lookup to see if this eventId was processed. Store processed event IDs with timestamps. If found, stop the scenario. If not found, continue and write the eventId into the processed log after a successful run.

This prevents duplicate handling when Xero retries deliveries.

  1. If webhook payload is partial, fetch the canonical invoice from Xero
  • Module: Xero, "Get Invoice" (or HTTP GET to Xero Invoices endpoint). Use resourceId to fetch the full invoice record.
  • Pull fields you need: InvoiceNumber, Contact (name, contactId), Date, DueDate, LineItems, SubTotal, Total, AmountDue, Status, Currency, and any attachments metadata.

Gotcha: Xero API rate limits and quotas exist. Avoid repeated full invoice fetches by caching recent invoice snapshots in your processed-log sheet or a lightweight KV store.

  1. Normalize and map fields for Notion
  • Convert Xero dates to ISO date strings Notion expects, normalise currency decimals, and flatten line items as a short summary field.
  • Build a Notion property mapping object. Typical Notion properties:
    • Invoice Number (title)
    • Xero Invoice ID (text)
    • Contact (text or relation)
    • Status (select)
    • Issue Date (date)
    • Due Date (date)
    • Amount (number)
    • Amount Due (number)
    • Currency (select)
    • Last Synced (date)
    • Source Link (URL to Xero invoice)

Note: Notion custom properties use internal IDs in the API. Get these from your database schema and keep them in a Make.com config variable.

  1. Upsert into Notion with rate-limit safety
  • Add a Notion "Search Database" step by Xero Invoice ID. If found, update that page with the new values. If not, create a new page.
  • To avoid hitting Notion rate limits when processing bursts, implement one of these strategies:
    • Batch writes using an internal queue and process at ~2 requests/sec average.
    • When multiple invoice events arrive in quick succession for the same org, aggregate them in-scenario and perform a single upsert for the latest state.
    • Respect Retry-After headers on 429 responses and implement exponential backoff with jitter.

Gotcha: frequent searches and updates per event can quickly hit Notion limits. Consolidate operations where possible and prefer searching by the indexed Xero Invoice ID property.

  1. Decide whether the event is actionable and post to Slack
  • Use a Make.com filter to decide which events merit a Slack alert. Examples:
    • Status changed to "AUTHORISED" or "PAID"
    • AmountDue > 0 and DueDate < today (overdue)
    • Payment applied or credit note created
  • For actionable events, add a Slack module to post a message. Keep the message short and include the Notion page link and Xero link.

Suggested Slack message template:

Invoice {{Invoice Number}} for {{Contact}} is {{Status}}
Amount due: {{AmountDue}} {{Currency}} • Due: {{DueDate}}
Notion: <https://www.notion.so/{{your_notion_page_id}}|Open record>
Xero: <{{xero_invoice_url}}|Open in Xero>

If the invoice is overdue include an owner mention or route to a collections channel.

  1. Append a reconciliation / audit row and mark the event processed
  • After Slack succeeds, append a row to your processed log (Google Sheets or Notion audit database) with: eventId, invoiceId, runId, result (created/updated/ignored), timestamp, and any error details.
  • If any step fails, route to a manual review path: send a detailed message to a private ops Slack channel and leave the event unmarked so you can retry after fix.
  1. Test thoroughly, then enable and monitor
  • Simulate INVOICE.CREATE and INVOICE.UPDATE events using Xero sandbox, verify dedupe, ensure Notion pages are correct, and confirm Slack messages are readable on mobile.
  • Monitor Make.com execution logs and Notion 429 responses in the first 72 hours, tune batching and backoff.

Real-World Business Scenario

A services business with 1,200 monthly invoices used this flow to keep its Notion finance database current. Previously finance operators manually pasted invoice snapshots into Notion and sent Slack messages about missed payments. After deploying webhook-driven sync, the ops lead stopped manual inserts, all overdue invoices landed in a collections Slack channel, and the Notion audit table provided a replayable history for month-end reconciliation. The company reduced missed reminders and shortened average days sales outstanding.

Common Variations

  • Backfill and reconciliation mode: run a scheduled Make.com scenario that pages through Xero invoices and upserts to Notion in controlled batches to bring historical data in line with the live webhook stream.
  • Attach invoice PDFs: if Xero attachments are required in Notion, fetch the PDF from Xero and upload it to Notion as a file block or store in Drive and link from Notion. Watch Notion rate limits when uploading large files.
  • Escalation branches: route invoices over a monetary threshold to a leadership Slack channel or create a ClickUp task for manual collection steps.

Where this connects with other patterns

If you need automated overdue notifications and a simple collections log, compare this pattern with our overdue-invoice alert workflow for QuickBooks in the post How to Automatically Alert Slack When a QuickBooks Online Invoice Becomes Overdue Using Make.com for similar routing and de-duplication ideas. For automated document generation such as client-facing invoices or PDFs, see our guide How to Generate a PDF from a Google Docs Template Using Make.com and Gmail which you can combine with this pipeline when you need attachments.

Wrapping up

You just built an event-driven pipeline that keeps Xero as the source of truth for invoices, writes canonical records into Notion, and surfaces only the events that need human attention in Slack. The key production considerations are idempotency, Notion rate-limit handling, and a reliable audit trail.

If you want this built for your stack, Olmec Dynamics implements Xero → Notion → Slack automations with production-grade idempotency, monitoring, and runbooks. See how we work at https://olmecdynamics.com.