Why an overdue invoice has not been paid: joining Xero and Gmail
It is the second Tuesday of the month. Your finance lead has the Xero aged receivables report open on one screen. Eleven invoices are past due, totalling a number she has already reported to you twice. The account manager for the largest of those, a 42-day-old invoice for £18,400, is sitting next to her with Gmail open, scrolling a thread with the client's operations director. The two of them are doing something that looks unremarkable and takes forty minutes: reading the report line by line, and for each line, searching the inbox for the customer name to work out what is actually going on.
Line four: the client's finance team bounced the invoice back because the PO number was missing. Line seven: the contact went on parental leave in June and nobody reassigned the approval. Line nine: there is no problem at all, the client pays on a 45-day cycle and the invoice is not late by their reckoning, only by the terms Xero has on file. Line eleven: the account manager sent a revised invoice by email three weeks ago and never asked accounts to void the original, so the overdue balance is a ghost.
Four different situations. In Xero, all eleven look identical: red, overdue, chase.
What Xero shows, and where it stops
Xero is precise about the things it is designed to be precise about. For each invoice you get the customer, the amount, the invoice date, the due date, the terms, the days outstanding, part payments applied, credit notes raised, the currency and the exchange rate at the time. Aged receivables buckets it into 30, 60, 90 and older. If you send invoices through Xero you also get a sent timestamp, and if online invoicing is on, you get a viewed timestamp, which is genuinely useful: it tells you whether the document reached a human's screen.
You can add an internal note to an invoice, and disciplined teams do. Xero also has repeating invoices, reminders and a history and notes trail on each record showing who edited what and when.
Where it stops is not a gap in the product. Xero knows the state of the ledger. It does not know why the ledger is in that state, because the reason lives in a conversation that happened somewhere else. "Overdue by 42 days" is an arithmetic fact derived from two dates. It carries no information about whether the client disputed the amount, whether your own account manager told them to hold off, or whether the invoice was superseded. The number is correct and the number is not the answer.
What Gmail shows, and where it stops
Gmail has the other half. Somewhere in the account manager's mailbox is a reply that says "we can't process this without a PO, can you reissue?" Somewhere in the shared accounts@ mailbox is an out of office from the approver. Somewhere is a thread where your own team agreed to extend terms for a quarter and never told finance.
Gmail search is good. from:client.com invoice after:2026/06/01 will find things. What Gmail cannot do is start from the list of overdue invoices, because it has never heard of them. It has no concept of an invoice number as a first class object, no idea which customers are on the aged receivables report this month, and no way to tell you which of your overdue invoices have zero correspondence attached to them at all, which is often the most interesting answer. Silence is evidence, and silence is exactly what a search engine cannot return.
There is also a scope problem. The reply that explains invoice INV-2291 is in one person's mailbox, not the finance lead's. She can search her own inbox all day and find nothing, because the conversation happened between the account manager and the client's operations director and was never forwarded.
Why the gap is structural, not a missing feature
Look at what each half actually is.
| Xero side | Gmail side | |
|---|---|---|
| Form of evidence | A record with fields | A sentence somebody wrote |
| Created by | A system, on a schedule | A person, in reaction to something |
| Structure | Invoice number, amount, dates, status | Free text, ambiguous, sometimes contradictory |
| Completeness | Every invoice exists | Only what someone bothered to write down |
| Query method | Filter and aggregate | Read and interpret |
This is the reason no reporting feature closes it. A report is an operation over records. The right hand column is not records. To use it you have to read a paragraph and decide what it means, and that decision is not a filter or a join condition, it is a judgement.
It is worth being precise about the neighbouring tools here, because they are good and the temptation is to assume one of them already covers this. Automation platforms like Zapier and Make are trigger based by design. They fire when something happens and move a record forward: invoice goes overdue, send a reminder, log it. That is a real and valuable job. But a trigger cannot answer a question about what already happened across a set of invoices, because there is no event to hang the question on. You are not asking for something to occur, you are asking what the accumulated state of eleven situations is.
BI tools sit on the other side. Power BI Copilot and Looker Conversational Analytics both do natural language over data, and do it well. Their reach is defined by what has been modelled: databases, warehouses, connected sources with schemas. An email thread where a client's ops director wrote "hold this until the new PO lands" is not in the warehouse and is not going to be. It is not that the question is too hard to phrase. The evidence is outside what the tool can see at all.
What the join actually requires
Two things, and they are different in kind.
The key is the easy part. An invoice number is a strong identifier: INV-2291 appears in the email body or the attachment filename, and matching it is reliable when it is there. When it is not, you fall back to weaker keys and combine them: the customer's email domain against the Xero contact record, the amount as a formatted string, the invoice date, the contact's name. Domain plus a date window catches most of what the invoice number misses.
The judgement is the hard part, and it does not reduce to matching. Given a thread, you have to decide which category it puts the invoice in: a dispute about the amount, a process blocker like a missing PO, an approver who is absent, a payment run that simply has not happened yet, an agreed extension, or an invoice that was superseded and should be voided. Those categories have very different consequences and none of them are signalled by a keyword you could grep for. "We'll get this sorted" means nothing on its own. It means something different in a thread where the client has already asked twice for a corrected document.
You also need the read to be recency aware. A dispute raised five weeks ago and resolved three weeks ago is not a live dispute, and a naive keyword match will tell you it is.
Doing it with Skopx
Skopx connects to nearly 1,000 SaaS tools, Xero and Gmail among them, and the question goes in as a sentence:
For every invoice on our Xero aged receivables over 30 days, find any Gmail thread that mentions the invoice number or is with that customer's domain in the last 60 days, and tell me the most likely reason each one is unpaid. Flag the ones with no correspondence at all.
What comes back is the eleven invoices with a reason attached to each, and a citation under each reason: the specific message, the sender, the date, so the finance lead can open the thread and check rather than trust. The ones with no correspondence come back as their own group, which is usually the group that matters most, because those are the invoices nobody has actually chased.
That is the answer to a question asked once. If it is the same forty minutes every month, the next step is a standing console. You describe it in the same chat and Skopx builds an internal app over the same two sources: a table of overdue invoices, each row carrying the amount, days outstanding, the inferred reason, the date of the last contact and who that contact was with, filterable by reason category, sorted by value at risk. Anyone on the team opens it and sees the current picture without asking anybody. Row level permissions mean an account manager sees their own accounts and the finance lead sees all of them.
The console reads. It can also act, in one specific way: a button next to a row that sends the chase email, or one that flags an invoice for voiding, which a person clicks and confirms. Nothing happens on its own.
What this does not do
It stores nothing. There is no new database of invoice reasons building up behind the console, no record created, no form to fill in. Every time you open it, it reads Xero and Gmail again and works out the answer from what is there now.
It does not run on a schedule and it does not alert you. If the console is not open, nothing is happening. Nobody gets a Slack message when an invoice tips into 60 days.
It does not write back to Xero on its own. Voiding the superseded invoice is still a person going into Xero and doing it.
The reasons are inferences, not facts, and some will be wrong. That is why every one arrives with the message it came from attached. Where a thread is genuinely ambiguous, you get told it is ambiguous rather than given a confident category. And where nobody wrote anything down, there is nothing to find. Skopx can tell you that eleven invoices have no correspondence, which is worth knowing, but it cannot tell you why an invoice nobody discussed is unpaid.
Skopx is $16 per seat per month on Team, including 2.3 million AI tokens per seat, or $5 for Solo. SOC 2 controls are in place.
Skopx Team
The Skopx engineering and product team