Customers
The /customers page lists every customer the predictions worker has built a baseline for. Each row tells you four things at once: who they are, how often they pay you, what they spent in the last 90 days, and roughly when (and how much) you should expect from them next.
Columns
| Column | What it shows |
|---|---|
| Customer | Display name (Xero contact name, or the contact's external id when none is recorded). |
| Cadence | Detected payment cadence (Weekly / Fortnightly / Monthly / Quarterly / Annual / None) + confidence %. |
| Last 90d | Sum of inflows attributed to this customer in the trailing 90 days. |
| Last seen | Days since the last invoice or payment from this customer. |
| Next Purchase Due | Date range pill ("May 18 – 24") + $X ± $Y amount pill — when and how much we expect from them next. |
| Risk | Churn-risk badge: Active (≤ 30 days), At risk (31–90 days), Churned (> 90 days or no signal). |
| ABC | Recursive 80/20 tier — A is the top 20% by revenue contribution, then B, then C. |
The Next Purchase Due column mirrors the dashboard's Top-customers card so the two surfaces read as one family — same chip pattern, same colours, same rounding. The amount pill reads as a real range: the central $X is the per-period baseline (median when we have one, mean otherwise — the same "robust centre" rule the deviation classifier follows), and the ± $Y is the half-IQR (or stddev on legacy rows). A row whose cadence is "None" (or whose projected next emission falls outside the 13-week horizon) renders an em-dash in this column rather than a fabricated guess.
Sorting
Every column header is sortable — click once to sort ascending, click again to flip to descending. The active sort key shows a ▲ / ▼ arrow next to the header label, and the URL captures the state in ?sort=...&dir=... so a refresh or share preserves the view.
| Sort key | Asc reads as | Desc reads as |
|---|---|---|
name | A → Z | Z → A |
cadence | Most-frequent first (Weekly → Annual → None) | Annual first, Weekly last |
spend | Lowest last-90d-spend first | Highest last-90d-spend first (default) |
churn | Most-recently-seen first | Most-stale first |
nextPurchase | Soonest-due first (projected horizon week) | Latest-due first; rows with no projection on top |
risk | Active customers first (then At risk, then Churned) | Churned first (then At risk, then Active) |
abc | A tier first | C tier first |
risk is different from churn — risk clusters customers by tier (Active / At risk / Churned), churn is a straight days-since-last-seen timeline. Use risk to find every at-risk customer regardless of exact age; use churn to find the single oldest unpaid contact.
Filters
A chip bar above the table lets you narrow the list by ABC tier, Cadence, and Risk — each group is multi-select within itself, and the three groups are intersected so an active filter set always reads as "show me only customers who are X and Y and Z."
Each chip is a link — clicking an active chip removes it from the filter, clicking an inactive one adds it. The URL is the source of truth (?abc=A,B&cadence=WEEKLY&risk=AT_RISK), so a filtered view is bookmarkable and shareable.
Filters survive sort clicks and pagination — the chip state never silently resets when you change the column order or step to the next page.
Search
The search input above the chip bar filters rows by display-name substring (case-insensitive). It submits as a plain form, so the search term lives in the URL (?q=acme) and a refresh / bookmark / share keeps the view. The Clear search link next to the input drops the search term while leaving the filter and sort state intact.
Drill-in
Clicking a customer's name opens the per-customer page — full history, manual payment-delay shifts, and the projected next 6 invoices with concrete dates. The per-customer baseline (mean / stddev) is computed automatically by the nightly worker; there is no manual override surface here. (Admins who need to override account-level baselines can do so on the Accounts → [account] drill-in.)
Excluded contacts
The "N excluded →" link in the page header opens the list of contacts an admin has manually excluded from customer analytics (refund-only contacts, internal transfers, etc.). Re-including a contact triggers the next nightly worker run to rebuild their baseline; until then the row reads "—" for cadence / spend / next purchase.