ARE THEY LEGIT

A public-interest reporting and intelligence platform designed specifically for the building industry

Impact of Rogue Builders on the Building Industry:

Estimated Victims

So far this Year

0

So far this Month

0

Today

0
(() => { const root = document.getElementById("atl-victims-widget"); if (!root) return; const SETTINGS = { locale: "en-GB", timezone: "Europe/London", initialAnimationMs: 1100 }; const METRIC = { annual: 1650000, monthly: 137500, daily: null }; const PERIODS = ["year", "month", "day"]; const state = { rendered: {} }; function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } function formatValue(value) { return Math.floor(value).toLocaleString(SETTINGS.locale); } function getValueEl(period) { return root.querySelector(`.atl-kpi-value[data-period="${period}"]`); } function getOffsetMillisecondsForTimeZone(utcMs, timeZone) { const parts = new Intl.DateTimeFormat("en-GB", { timeZone, timeZoneName: "shortOffset" }).formatToParts(new Date(utcMs)); const tzNamePart = parts.find(part => part.type === "timeZoneName"); const tzName = tzNamePart ? tzNamePart.value : "GMT"; if (tzName === "GMT" || tzName === "UTC") return 0; const match = tzName.match(/GMT([+-])(\d{1,2})(?::?(\d{2}))?/); if (!match) return 0; const sign = match[1] === "-" ? -1 : 1; const hours = Number(match[2] || 0); const minutes = Number(match[3] || 0); return sign * ((hours * 60) + minutes) * 60 * 1000; } function zonedDateTimeToUtcMs(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0, timeZone = SETTINGS.timezone) { const naiveUtcMs = Date.UTC(year, month - 1, day, hour, minute, second, millisecond); let offset = getOffsetMillisecondsForTimeZone(naiveUtcMs, timeZone); let utcMs = naiveUtcMs - offset; const refinedOffset = getOffsetMillisecondsForTimeZone(utcMs, timeZone); if (refinedOffset !== offset) { utcMs = naiveUtcMs - refinedOffset; } return utcMs; } function getUKNowParts() { const now = new Date(); const parts = new Intl.DateTimeFormat("en-GB", { timeZone: SETTINGS.timezone, year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hourCycle: "h23" }).formatToParts(now); const out = {}; for (const part of parts) { if (part.type !== "literal") out[part.type] = Number(part.value); } return { year: out.year, month: out.month, day: out.day, nowUtcMs: now.getTime() }; } function getNextDayDate(year, month, day) { const d = new Date(Date.UTC(year, month - 1, day)); d.setUTCDate(d.getUTCDate() + 1); return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() }; } function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); } function getDailyBaseline(year) { if (typeof METRIC.daily === "number" && !Number.isNaN(METRIC.daily)) { return METRIC.daily; } return METRIC.annual / (isLeapYear(year) ? 366 : 365); } function getProgressValue(totalForPeriod, startUtcMs, endUtcMs, nowUtcMs) { const elapsed = clamp(nowUtcMs - startUtcMs, 0, endUtcMs - startUtcMs); const duration = Math.max(endUtcMs - startUtcMs, 1); return totalForPeriod * (elapsed / duration); } function calculateTargets() { const now = getUKNowParts(); const yearStartUtcMs = zonedDateTimeToUtcMs(now.year, 1, 1, 0, 0, 0, 0); const nextYearStartUtcMs = zonedDateTimeToUtcMs(now.year + 1, 1, 1, 0, 0, 0, 0); const monthStartUtcMs = zonedDateTimeToUtcMs(now.year, now.month, 1, 0, 0, 0, 0); const nextMonth = now.month === 12 ? { year: now.year + 1, month: 1 } : { year: now.year, month: now.month + 1 }; const nextMonthStartUtcMs = zonedDateTimeToUtcMs(nextMonth.year, nextMonth.month, 1, 0, 0, 0, 0); const dayStartUtcMs = zonedDateTimeToUtcMs(now.year, now.month, now.day, 0, 0, 0, 0); const nextDay = getNextDayDate(now.year, now.month, now.day); const nextDayStartUtcMs = zonedDateTimeToUtcMs(nextDay.year, nextDay.month, nextDay.day, 0, 0, 0, 0); const dailyBaseline = getDailyBaseline(now.year); return { year: getProgressValue(METRIC.annual, yearStartUtcMs, nextYearStartUtcMs, now.nowUtcMs), month: getProgressValue(METRIC.monthly, monthStartUtcMs, nextMonthStartUtcMs, now.nowUtcMs), day: getProgressValue(dailyBaseline, dayStartUtcMs, nextDayStartUtcMs, now.nowUtcMs) }; } function samePattern(a, b) { if (!a || a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { const aDigit = /\d/.test(a[i]); const bDigit = /\d/.test(b[i]); if (aDigit !== bDigit) return false; } return true; } function buildNumber(el, formatted) { el.innerHTML = ""; for (const char of formatted) { if (/\d/.test(char)) { const slot = document.createElement("span"); slot.className = "digit-slot"; const current = document.createElement("span"); current.className = "digit-current"; current.textContent = char; slot.appendChild(current); el.appendChild(slot); } else { const sep = document.createElement("span"); sep.className = "separator"; sep.textContent = char; el.appendChild(sep); } } el.dataset.formatted = formatted; } function animateDigitChange(slot, newChar) { const current = slot.querySelector(".digit-current"); if (!current || current.textContent === newChar) return; if (slot._timer) clearTimeout(slot._timer); const oldChar = current.textContent; slot.innerHTML = ""; const oldDigit = document.createElement("span"); oldDigit.className = "digit-old"; oldDigit.textContent = oldChar; const newDigit = document.createElement("span"); newDigit.className = "digit-new"; newDigit.textContent = newChar; slot.appendChild(oldDigit); slot.appendChild(newDigit); slot._timer = setTimeout(() => { slot.innerHTML = ""; const replacement = document.createElement("span"); replacement.className = "digit-current"; replacement.textContent = newChar; slot.appendChild(replacement); }, 180); } function updateNumber(el, value) { const formatted = formatValue(value); const previous = el.dataset.formatted || ""; if (!samePattern(previous, formatted)) { buildNumber(el, formatted); return; } const children = [...el.childNodes]; let i = 0; for (const node of children) { const newChar = formatted[i++]; if (/\d/.test(newChar)) { animateDigitChange(node, newChar); } else { node.textContent = newChar; } } el.dataset.formatted = formatted; } function setPlainText(el, value) { el.textContent = formatValue(value); } function runInitialAnimation() { const targets = calculateTargets(); const startTime = performance.now(); function frame(now) { const progress = clamp((now - startTime) / SETTINGS.initialAnimationMs, 0, 1); const eased = easeOutCubic(progress); for (const period of PERIODS) { const el = getValueEl(period); setPlainText(el, targets[period] * eased); } if (progress < 1) { requestAnimationFrame(frame); return; } for (const period of PERIODS) { const el = getValueEl(period); const finalValue = Math.floor(targets[period]); buildNumber(el, formatValue(finalValue)); state.rendered[period] = finalValue; } startLiveUpdates(); } requestAnimationFrame(frame); } function renderLive() { const targets = calculateTargets(); for (const period of PERIODS) { const nextValue = Math.floor(targets[period]); if (state.rendered[period] !== nextValue) { updateNumber(getValueEl(period), nextValue); state.rendered[period] = nextValue; } } } function scheduleNextSecondTick() { const delay = 1000 - (Date.now() % 1000); setTimeout(() => { renderLive(); scheduleNextSecondTick(); }, delay); } function startLiveUpdates() { renderLive(); scheduleNextSecondTick(); } runInitialAnimation(); })();

Money Lost

So far this Year

£0

So far this Month

£0

Today

£0
(() => { const root = document.getElementById("atl-money-widget"); if (!root) return; const SETTINGS = { locale: "en-GB", timezone: "Europe/London", initialAnimationMs: 1100 }; const METRIC = { annual: 2860000000, monthly: 238333333, daily: 7835616 }; const PERIODS = ["year", "month", "day"]; const state = { rendered: {} }; function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } function floorToOneDecimal(value) { return Math.floor(value * 10) / 10; } function formatValue(value) { const abs = Math.abs(value); if (abs >= 1000000000) { const n = floorToOneDecimal(value / 1000000000); return `£${n.toFixed(1).replace(/\.0$/, "")}B`; } if (abs >= 1000000) { const n = floorToOneDecimal(value / 1000000); return `£${n.toFixed(1).replace(/\.0$/, "")}M`; } if (abs >= 1000) { const n = floorToOneDecimal(value / 1000); return `£${n.toFixed(1).replace(/\.0$/, "")}K`; } return `£${Math.floor(value).toLocaleString(SETTINGS.locale)}`; } function getValueEl(period) { return root.querySelector(`.atl-kpi-value[data-period="${period}"]`); } function getOffsetMillisecondsForTimeZone(utcMs, timeZone) { const parts = new Intl.DateTimeFormat("en-GB", { timeZone, timeZoneName: "shortOffset" }).formatToParts(new Date(utcMs)); const tzNamePart = parts.find(part => part.type === "timeZoneName"); const tzName = tzNamePart ? tzNamePart.value : "GMT"; if (tzName === "GMT" || tzName === "UTC") return 0; const match = tzName.match(/GMT([+-])(\d{1,2})(?::?(\d{2}))?/); if (!match) return 0; const sign = match[1] === "-" ? -1 : 1; const hours = Number(match[2] || 0); const minutes = Number(match[3] || 0); return sign * ((hours * 60) + minutes) * 60 * 1000; } function zonedDateTimeToUtcMs(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0, timeZone = SETTINGS.timezone) { const naiveUtcMs = Date.UTC(year, month - 1, day, hour, minute, second, millisecond); let offset = getOffsetMillisecondsForTimeZone(naiveUtcMs, timeZone); let utcMs = naiveUtcMs - offset; const refinedOffset = getOffsetMillisecondsForTimeZone(utcMs, timeZone); if (refinedOffset !== offset) { utcMs = naiveUtcMs - refinedOffset; } return utcMs; } function getUKNowParts() { const now = new Date(); const parts = new Intl.DateTimeFormat("en-GB", { timeZone: SETTINGS.timezone, year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hourCycle: "h23" }).formatToParts(now); const out = {}; for (const part of parts) { if (part.type !== "literal") out[part.type] = Number(part.value); } return { year: out.year, month: out.month, day: out.day, nowUtcMs: now.getTime() }; } function getNextDayDate(year, month, day) { const d = new Date(Date.UTC(year, month - 1, day)); d.setUTCDate(d.getUTCDate() + 1); return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() }; } function getProgressValue(totalForPeriod, startUtcMs, endUtcMs, nowUtcMs) { const elapsed = clamp(nowUtcMs - startUtcMs, 0, endUtcMs - startUtcMs); const duration = Math.max(endUtcMs - startUtcMs, 1); return totalForPeriod * (elapsed / duration); } function calculateTargets() { const now = getUKNowParts(); const yearStartUtcMs = zonedDateTimeToUtcMs(now.year, 1, 1, 0, 0, 0, 0); const nextYearStartUtcMs = zonedDateTimeToUtcMs(now.year + 1, 1, 1, 0, 0, 0, 0); const monthStartUtcMs = zonedDateTimeToUtcMs(now.year, now.month, 1, 0, 0, 0, 0); const nextMonth = now.month === 12 ? { year: now.year + 1, month: 1 } : { year: now.year, month: now.month + 1 }; const nextMonthStartUtcMs = zonedDateTimeToUtcMs(nextMonth.year, nextMonth.month, 1, 0, 0, 0, 0); const dayStartUtcMs = zonedDateTimeToUtcMs(now.year, now.month, now.day, 0, 0, 0, 0); const nextDay = getNextDayDate(now.year, now.month, now.day); const nextDayStartUtcMs = zonedDateTimeToUtcMs(nextDay.year, nextDay.month, nextDay.day, 0, 0, 0, 0); return { year: getProgressValue(METRIC.annual, yearStartUtcMs, nextYearStartUtcMs, now.nowUtcMs), month: getProgressValue(METRIC.monthly, monthStartUtcMs, nextMonthStartUtcMs, now.nowUtcMs), day: getProgressValue(METRIC.daily, dayStartUtcMs, nextDayStartUtcMs, now.nowUtcMs) }; } function samePattern(a, b) { if (!a || a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { const aDigit = /\d/.test(a[i]); const bDigit = /\d/.test(b[i]); if (aDigit !== bDigit) return false; } return true; } function buildNumber(el, formatted) { el.innerHTML = ""; for (const char of formatted) { if (/\d/.test(char)) { const slot = document.createElement("span"); slot.className = "digit-slot"; const current = document.createElement("span"); current.className = "digit-current"; current.textContent = char; slot.appendChild(current); el.appendChild(slot); } else { const sep = document.createElement("span"); sep.className = "separator"; sep.textContent = char; el.appendChild(sep); } } el.dataset.formatted = formatted; } function animateDigitChange(slot, newChar) { const current = slot.querySelector(".digit-current"); if (!current || current.textContent === newChar) return; if (slot._timer) clearTimeout(slot._timer); const oldChar = current.textContent; slot.innerHTML = ""; const oldDigit = document.createElement("span"); oldDigit.className = "digit-old"; oldDigit.textContent = oldChar; const newDigit = document.createElement("span"); newDigit.className = "digit-new"; newDigit.textContent = newChar; slot.appendChild(oldDigit); slot.appendChild(newDigit); slot._timer = setTimeout(() => { slot.innerHTML = ""; const replacement = document.createElement("span"); replacement.className = "digit-current"; replacement.textContent = newChar; slot.appendChild(replacement); }, 180); } function updateNumber(el, value) { const formatted = formatValue(value); const previous = el.dataset.formatted || ""; if (!samePattern(previous, formatted)) { buildNumber(el, formatted); return; } const children = [...el.childNodes]; let i = 0; for (const node of children) { const newChar = formatted[i++]; if (/\d/.test(newChar)) { animateDigitChange(node, newChar); } else { node.textContent = newChar; } } el.dataset.formatted = formatted; } function setPlainText(el, value) { el.textContent = formatValue(value); } function runInitialAnimation() { const targets = calculateTargets(); const startTime = performance.now(); function frame(now) { const progress = clamp((now - startTime) / SETTINGS.initialAnimationMs, 0, 1); const eased = easeOutCubic(progress); for (const period of PERIODS) { const el = getValueEl(period); setPlainText(el, targets[period] * eased); } if (progress < 1) { requestAnimationFrame(frame); return; } for (const period of PERIODS) { const el = getValueEl(period); const finalValue = Math.floor(targets[period]); buildNumber(el, formatValue(finalValue)); state.rendered[period] = finalValue; } startLiveUpdates(); } requestAnimationFrame(frame); } function renderLive() { const targets = calculateTargets(); for (const period of PERIODS) { const nextValue = Math.floor(targets[period]); if (state.rendered[period] !== nextValue) { updateNumber(getValueEl(period), nextValue); state.rendered[period] = nextValue; } } } function scheduleNextSecondTick() { const delay = 1000 - (Date.now() % 1000); setTimeout(() => { renderLive(); scheduleNextSecondTick(); }, delay); } function startLiveUpdates() { renderLive(); scheduleNextSecondTick(); } runInitialAnimation(); })();

(Illustrative estimate based on Federation of Master Builders 2025 research - not live ATL reporting data. survey results)

'ATL Victim Survey Report 2026' now available for download! (114 validated entries) Download here

Rogue-builder harm is substantial across the UK, with Federation of Master Builders research estimating £14.3bn in losses over five years.Only 3% of fraud reports to Action Fraud are investigated, and less than 1% lead to prosecution*.Are They Legit (ATL) is building the missing layer: Serious contractor harm can be difficult to distinguish from poor workmanship or ordinary disputes because relevant building-specific risk signals are not consistently captured, while reports and evidence remain fragmented across different systems and sectors. ATL is being developed as a specialist reporting and intelligence layer to help connect those signals, identify patterns and support appropriate organisations with proportionate, actionable intelligence.

The problem: fragmented reporting hides repeat harm

Existing reporting routes capture different parts of the picture. Building-specific risk signals are not consistently collected, and information reported to different organisations is rarely connected. This can make it difficult to distinguish poor workmanship and ordinary contractual disputes from repeated deception or fraud, and limits effective contractor-specific triage.


Why ATL? (What we’re building)

ATL is being developed to complement existing reporting, enforcement and fraud-prevention systems. It structures building-specific reports, links relevant evidence and identifiers, applies evidence thresholds and human review, and produces proportionate intelligence outputs for appropriate recipients.ATL will help people and institutions:

  • Report building-work harm through a structured reporting flow designed specifically for domestic renovation projects

  • Verify patterns through evidence-led thresholds (reducing false positives and protecting honest operators)

  • Identify patterns of serious repeat harm earlier so appropriate organisations can prioritise higher-risk cases more effectively

  • Prevent repeat victimisation through awareness, alerts, and intelligence (not just reactive “after the damage” dispute handling)

What ATL is not

  • Another “find a tradesperson” directory (e.g., Checkatrade/MyBuilder style)

  • A public “blacklist” or complaint wall

  • A replacement for mediation/arbitration for minor contractual disagreements or snagging disputes

  • A tool to punish honest mistakes, low skill, or normal misunderstandings

  • ATL is not an enforcement body and does not determine criminal liability

  • ATL is not intended to replace Report Fraud, Trading Standards, policing, banks, regulators or existing dispute-resolution routes.

What ATL is

  • A specialist, sector-specific reporting and risk-intelligence layer designed to complement existing systems.

  • An evidence-led process using defined thresholds and human review before any alert or information-sharing escalation.

  • A way to turn fragmented reports and evidence into structured, recipient-specific intelligence.

  • A public-interest model designed around fairness, correction routes, right of reply, auditability and appropriate governance.

How ATL works (high level)

ATL is designed to be credible, proportionate, and safe, not sensational.


1) Structured reporting built for building work
Reports capture the details that matter in construction fraud patterns: who, what, where, when, amounts paid, method of payment, identifiers, evidence types, and project context.
2) Evidence-based thresholds and severity tiers
ATL is designed to avoid “blacklisting” dynamics by using thresholds and tiers. It aims to distinguish:

  • Normal disputes and snagging issues from

  • Repeat serious rogue behaviour (where patterns and evidence indicate public harm)

3) Pattern recognition across reports and data
ATL focuses on repeat signals over time, not single allegations. The goal is to identify serious repeat patterns and emerging risks earlier.
4) Safeguards that protect legitimate trades
ATL anticipates the key risks (defamation, misuse, unfair competition concerns) and is built around mitigations such as:

  • Evidence thresholds

  • Severity tiers

  • Correction routes / right of reply principles

  • Governance and oversight design


The commercial case: cost savings and reduced demand on public services

ATL is being designed to test whether better structured, linked and prioritised intelligence can reduce duplicated reporting, improve triage and support earlier intervention.It helps reduce demand across:

  • Public reporting and enforcement pathways through better structured and prioritised intelligence

  • Banks and fraud-prevention teams through improved contractor-linked pattern visibility

  • Existing investigation and resolution routes through earlier identification of serious repeat harm

  • Local authority services through more actionable contractor-specific intelligence


Our Vision

Our aim is to develop a publicly accessible platform that proactively reduces demand on public services and authorities by:

  1. Structuring and connecting fragmented building-specific reports, evidence and risk signals

  2. Identifying serious repeat patterns through proportionate evidence thresholds and human review

  3. Producing useful, recipient-specific intelligence for appropriate organisations

  4. Protecting legitimate trades through fairness safeguards, correction routes, auditability and appropriate governance

ATL's aim is to complement existing national and local systems by connecting fragmented building-specific reports and evidence, identifying serious repeat patterns and routing useful intelligence to organisations already responsible for prevention, investigation and enforcement.


Current stage - prototype and operating model validation. ATL is currently being tested with a small group of practitioners and specialists. The next stage is to co-design one or two recipient pathways using synthetic, historical or de-identified cases, validate the evidence thresholds and intelligence outputs, and assess whether a tightly controlled pilot is justified.


— The ATL Team
Rebuilding Trust in the Building Industry
Stay updated!
Don't miss out — Follow us on social media for the latest news, updates, and exclusive content:


*Sources:
[1. Which? - Exclusive: more than 96% of reported fraud cases go unsolved]
[2. BBC - Fraud victims 'failed' as criminals 'operate with impunity' - report]
[3. Police Foundation - Fraud is not prioritised by the police, report finds]
[4. UK Parliament - Justice response inadequate to meet scale of fraud epidemic]
[5. UK Parliament - PAC: UK risks being seen as “haven for fraudsters” where fraud is “everyone’s problem but no one’s priority"].
[6. UK Parliament - Domestic Building Works (Consumer Protection) Bill]
[7. Cowboy builders could be costing Brits a staggering £14.3 Billion ].


Are They Legit (ATL) – Privacy Policy
Last updated: August 2025
1. Who We Are
This website is operated by Are They Legit (ATL). Our mission is to reduce contractor-related harm through public prevention resources and the development of structured reporting and intelligence tools.
Contact: [email protected]
2. What Data We Collect
a. Voluntary Data You Provide
- Email address (via sign-up or contact forms)
- Any messages you send us
b. Automatically Collected Data
We use Plausible Analytics, a privacy-friendly tool, to collect anonymous website usage statistics. This includes:
- Page views
- Referring websites (e.g. Facebook Groups, email campaigns)
- Approximate location (country/city, without storing IP addresses)
No cookies are used for analytics, and we do not collect or store personal data such as IP addresses or device fingerprints. We do not knowingly collect data from children under 16.
3. Why We Collect Your Data

PurposeLegal Basis
To send ATL fraud awareness updatesConsent (sign-up form)
To reply to contact submissionsLegitimate interest
To understand how the site is usedLegitimate interest (via Plausible Analytics)

4. How We Use Cookies
We currently do not use tracking or advertising cookies.
Our analytics provider, Plausible, does not use cookies or collect personal data, so no consent banner is required.
We only use essential cookies if needed for form or session functionality.
5. Data Sharing
We do not sell or share your data for marketing purposes.
We may use GDPR-compliant third-party services, including:
- Plausible Analytics (for anonymous site usage tracking)
- Brevo (for sending fraud awareness updates)
- Web hosting provider (Carrd or similar)
All partners follow data protection standards under UK GDPR.
6. Data Storage
- Form submissions are stored securely by our email or hosting provider
- Analytics data is aggregated and anonymised by Plausible and never includes personal identifiers
7. Data Retention
- Newsletter sign-up: until unsubscribed or deleted
- Contact submissions: up to 12 months
- Analytics data: Aggregated only, no personal retention
You may request deletion at any time.
8. Your Rights Under UK GDPR
- Access, correct, delete your data
- Restrict or object to processing
- Withdraw consent
- Lodge a complaint with the ICO
To exercise these rights: [email protected]
9. External Links
Our site may link to other websites (e.g. fraud reporting agencies). We are not responsible for their privacy policies or data practices.
10. Changes to This Policy
We may update this policy. Revisions will be posted here with the updated date.
Cookies – Summary
We use no analytics or tracking cookies.
Analytics data is collected without cookies or personal information using Plausible.
If essential cookies are added (e.g. for forms), they will not require consent.
Contact Us
If you have any concerns about this policy or your personal data, contact us at:
📧 [email protected]

Confirm your email address

Please verify your email address using the verification email we've just sent you to get your free 'Contractor Vetting Checklist' and support ATL.

*Check junk folder if email not received

— The ATL Team
Rebuilding Trust in the Building Industry
Please share aretheylegit.co.uk with friends, family and colleagues to help spread awareness of our initiative!

Thank you for your support!

Thank you for supporting ATL. We have heard from hundreds of people affected by serious building-work problems, many of whom describe difficulty navigating fragmented reporting and resolution routes. ATL is being developed to improve how building-specific evidence is structured, linked and routed while supporting the organisations already responsible for prevention, investigation and enforcement.


Every sign-up helps demonstrate public interest and informs our ongoing stakeholder validation.

Download your free 'Contractor Vetting Checklist' here.

Want to do more?
Support ATL's early-stage research, development and public-interest work.


Your donation fuels development, public outreach, and the tools people and businesses need to protect themselves, and each other.

— The ATL Team
Rebuilding Trust in the Building Industry
Stay updated!
Don't miss out — Follow us on social media for the latest news, updates, and exclusive content:

Please share aretheylegit.co.uk with friends, family and colleagues to help spread awareness of our initiative!

ATL Victim Report 2026

This report summarises findings from 114 validated rogue builder victims across the UK, highlighting the most common tactics used, the true scale of property and financial harm, and the burden placed on banks, authorities, and the courts. It presents clear, data-led insights into why so many cases go unresolved, and why a preventative, evidence-based consumer protection platform is urgently needed.


Top-line findings are stark:
- 66% found the trader online
- £2.77m total damages (114 victims)
- 100% Police not resolved
- 98% Action Fraud not resolved
- 96% Trading Standards not resolved
- 90% bank “no action / civil dispute”
- 54% still not resolved after 2 years


Download the complete 'ATL Victim Survey Report 2026' here.


These findings are drawn from 114 validated responses to ATL's exploratory victim survey. The respondents were self-selected, so the results describe this group and should not be treated as representative of all UK contractor-fraud cases or as national performance measures for individual organisations.

PREVENTION

WARNING SIGNS

ACTIVE SCAM

ACTIVE SCAM

----------------------------------------------------------------------------------------------------------------------------------------

ATL Trade Survey Poster


Help us ensure that honest trades are not left out of the solutions being proposed for the industry. Download and share our trade survey now:

ATL Trade Survey Poster.jpg
ATL Trade Survey Poster.pdf