August 25, 2026
Stop Pasting Your Company’s Data Into AI Chats

By Justo Herrero
6 min read
I work with data every day. Queries, schemas, pipelines, the usual stuff. And for the last couple of years I've watched the same thing happen over and over: someone runs a query, gets a result, and pastes it straight into ChatGPT or Claude to "understand it better."
Nobody thinks twice about it. It feels like CTRL+C, CTRL+V. No different from pasting into a spreadsheet.
It's not. Every prompt you send leaves your machine and lands on someone else's servers. And once it's there, you don't control it anymore.
This isn't a piece about being scared of AI. I use it constantly and it makes me faster at almost everything. This is about the handful of habits that let you get all that value without handing over data you'd never put in an email to a stranger.
TL;DR — the 60-second version
🔒 Security — Every prompt lands on third-party servers. Treat it like it could become public.
📋 Structure, not content — Share schemas, distributions, aggregates. Never raw records, PII, or row-level query output.
🎭 Synthetic data — Need row-level examples? Generate fake data with the same shape. Ten fake rows beat a thousand real ones.
⚠️ Validate — AI output looks confident but can be wrong. Match your scrutiny to the stakes.
🚫 Don't copy-paste — Pasting internal system output into external AI is the #1 uncontrolled leak.
💰 Cost — Every AI query burns compute. Check the dashboard before you ask.
Golden rule:_ Share the structure. Share the statistics. Share the problem. Keep the records._
The part people skip
Every AI conversation is processed on third-party infrastructure. That's true even with the best contracts and the best intentions from the provider. A few things follow from that, whether you think about them or not:
Your input sits on servers you don't control, even if briefly. If that provider has a breach, whatever you pasted is part of what's exposed. Some providers use conversations to improve their models unless you've explicitly opted out, which means your schema, your business logic, your specific way of solving a problem, can end up shaping something public. Retention policies are all over the place, and "deleted" rarely means gone from every backup. And every plugin, extension, or integration in the chain is one more door that could be compromised without you ever knowing.
None of this means the provider is out to get you. It means their infrastructure is now part of your attack surface, whether you meant it to be or not.
My rule of thumb: if you wouldn't put it in an email to someone outside the company, don't put it in a prompt.
What's actually safe to share
This is the part that surprises people. AI barely needs your real data to be useful. What it needs is structure.
Once you see the split laid out like this, it's obvious. But in the moment, three questions deep into a debugging session, it's very easy to just paste the output and move on.
Five things I actually do
1. Share metadata, not data.
Instead of pasting query results, I describe the table:
Table: bookings
Rows: 2.3M
Distinct destinations: 340 (top 5 = 45% of records)
Date range: 2019-01-01 to 2026-08-01
Nulls: destination 0.2%, departure_date 0.0%
Status split: confirmed 72%, cancelled 18%, pending 10%Table: bookings
Rows: 2.3M
Distinct destinations: 340 (top 5 = 45% of records)
Date range: 2019-01-01 to 2026-08-01
Nulls: destination 0.2%, departure_date 0.0%
Status split: confirmed 72%, cancelled 18%, pending 10%That block gives an AI model more useful context than a screenshot of 50 rows ever would, and it exposes nothing.
2. Use synthetic data that mirrors reality.
When I actually need row-level detail (testing a join, a transformation, an edge case), I generate fake rows that match the real distributions:
import faker, random
fake = faker.Faker()
STATUSES = ['confirmed'] * 72 + ['cancelled'] * 18 + ['pending'] * 10
DESTINATIONS = ['Mallorca', 'Tenerife', 'Cancun', 'Antalya', 'Crete']
sample = [{
"booking_id": f"BK{fake.unique.random_number(digits=8)}",
"customer_id": fake.random_int(min=10000, max=99999),
"destination": random.choice(DESTINATIONS),
"departure_date": fake.date_between(start_date='-1y', end_date='+6m'),
"status": random.choice(STATUSES),
} for _ in range(10)]import faker, random
fake = faker.Faker()
STATUSES = ['confirmed'] * 72 + ['cancelled'] * 18 + ['pending'] * 10
DESTINATIONS = ['Mallorca', 'Tenerife', 'Cancun', 'Antalya', 'Crete']
sample = [{
"booking_id": f"BK{fake.unique.random_number(digits=8)}",
"customer_id": fake.random_int(min=10000, max=99999),
"destination": random.choice(DESTINATIONS),
"departure_date": fake.date_between(start_date='-1y', end_date='+6m'),
"status": random.choice(STATUSES),
} for _ in range(10)]Ten synthetic rows do the job better than a thousand real ones, because I control exactly what they reveal, which is nothing.
3. Aggregate before you paste anything.
-- Run this locally, never paste the raw output
SELECT status, COUNT(*), AVG(total_amount)
FROM bookings
GROUP BY status;
-- Share only the summary
confirmed: 1.65M rows, avg €1,240
cancelled: 414K rows, avg €980
pending: 230K rows, avg €1,100-- Run this locally, never paste the raw output
SELECT status, COUNT(*), AVG(total_amount)
FROM bookings
GROUP BY status;
-- Share only the summary
confirmed: 1.65M rows, avg €1,240
cancelled: 414K rows, avg €980
pending: 230K rows, avg €1,100The AI gets the full shape of the problem from three lines instead of two million rows.
4. Describe the problem instead of dumping the data.
This is the one people skip most, and it's the one that actually gets you better answers.
❌ "Here are 500 rows, find the pattern."
✅ "I have 2.3M rows. Duplicates started appearing after a March migration. Same email, different ID. The newer ID always has a different source system. What reconciliation strategies exist?"
The second version gets you a sharper answer and shares zero real records.
5. Use diagrams instead of dumps.
A ten-line entity relationship diagram communicates a data model faster than fifty lines of schema description, and there's nothing in it to leak:
Full data model context. Zero PII. Zero real data. Maximum insight for the AI.
A workflow, not a habit
The techniques above only work if you treat exploration as a phase with a clear end, not an open-ended chat that accumulates context forever.
First, get the AI to understand the problem using structure alone. No real records at any point.
Second, explore approaches. Query strategies, transformation logic, architecture options, tool choices. This is where AI is genuinely useful as a reasoning partner, and none of it requires real data either.
Third, validate with synthetic examples. Generate rows that mirror your real patterns, including the edge cases, and let the AI write or check code against them before you touch production.
Fourth, carry forward the conclusion, not the conversation.
Start a new chat, summarise the decision, and leave the exploration context behind. You don't need it anymore, and dragging it forward just adds cost and risk for no benefit.
Queries leak more than you think
Here's the one that catches people off guard: even a query with zero real data in it can expose exactly how your business works.
Table and column names tell someone your data model.
JOIN conditions tell them how your systems connect. WHERE clauses reveal your business rules, eligibility criteria, segmentation logic. CASE statements are often your pricing tiers or your scoring algorithm laid bare.
-- ❌ BEFORE — reveals refund policy, loyalty tiers, cooling-off period
SELECT b.booking_id, c.loyalty_tier,
CASE
WHEN cr.reason_code IN ('MEDICAL', 'BEREAVEMENT') THEN 'full_refund'
WHEN c.loyalty_tier IN ('gold', 'platinum')
AND b.departure_date - CURRENT_DATE > 48 THEN 'full_refund'
WHEN b.departure_date - CURRENT_DATE > 14 THEN 'partial_refund'
ELSE 'credit_only'
END AS refund_type
FROM bookings b
JOIN customers c ON b.customer_id = c.id
JOIN cancellation_reasons cr ON b.cancel_reason_id = cr.id;
-- ✅ AFTER — same pattern, AI can still help optimise, no logic exposed
SELECT o.order_id, u.user_segment,
CASE
WHEN r.reason_code IN ('REASON_A', 'REASON_B') THEN 'outcome_1'
WHEN u.user_segment IN ('segment_high', 'segment_premium')
AND o.target_date - CURRENT_DATE > THRESHOLD_A THEN 'outcome_1'
WHEN o.target_date - CURRENT_DATE > THRESHOLD_B THEN 'outcome_2'
ELSE 'outcome_3'
END AS result_type
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN reasons r ON o.reason_id = r.id;-- ❌ BEFORE — reveals refund policy, loyalty tiers, cooling-off period
SELECT b.booking_id, c.loyalty_tier,
CASE
WHEN cr.reason_code IN ('MEDICAL', 'BEREAVEMENT') THEN 'full_refund'
WHEN c.loyalty_tier IN ('gold', 'platinum')
AND b.departure_date - CURRENT_DATE > 48 THEN 'full_refund'
WHEN b.departure_date - CURRENT_DATE > 14 THEN 'partial_refund'
ELSE 'credit_only'
END AS refund_type
FROM bookings b
JOIN customers c ON b.customer_id = c.id
JOIN cancellation_reasons cr ON b.cancel_reason_id = cr.id;
-- ✅ AFTER — same pattern, AI can still help optimise, no logic exposed
SELECT o.order_id, u.user_segment,
CASE
WHEN r.reason_code IN ('REASON_A', 'REASON_B') THEN 'outcome_1'
WHEN u.user_segment IN ('segment_high', 'segment_premium')
AND o.target_date - CURRENT_DATE > THRESHOLD_A THEN 'outcome_1'
WHEN o.target_date - CURRENT_DATE > THRESHOLD_B THEN 'outcome_2'
ELSE 'outcome_3'
END AS result_type
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN reasons r ON o.reason_id = r.id;Same structure. Same help from the AI. Zero business logic exposed. That's the whole trick: keep the pattern, change the domain, replace magic numbers with named constants.
AI is confident. That's not the same as correct
The output looks clean. It looks certain. That doesn't mean it's right, and unlike a query you wrote yourself, you didn't control every join and every filter that went into it.
I use three tiers to decide how much scrutiny something needs:
The red flags are usually obvious once you're looking for them: numbers that don't match your gut feel, results that are suspiciously round, an AI that can't explain how it got a number, an answer that changes when you rephrase the same question slightly, a number that contradicts a dashboard everyone already trusts.
And if you find yourself asking the same question every week, that's not an AI use case anymore. That's a dashboard you haven't built yet.
Nobody talks about the cost
This one's less about risk and more about waste, but it's worth saying because almost nobody accounts for it.
Every AI query is compute you pay for on the spot, not a dashboard that's already rendered and sits there for free:
1️⃣ LLM inference → your question gets translated to SQL
2️⃣ Query execution → the SQL runs against real compute (the expensive part)
3️⃣ Result formatting → response comes back in readable form1️⃣ LLM inference → your question gets translated to SQL
2️⃣ Query execution → the SQL runs against real compute (the expensive part)
3️⃣ Result formatting → response comes back in readable formA narrow question costs fractions of a unit. A broad one that scans millions of rows costs a lot more, and if ten people on your team ask a version of the same broad question every day, it adds up into a real number by the end of the month.
Check if a dashboard already answers it before you ask. Be specific: "revenue from X in July" is cheaper than "show me revenue by everything." Save the answer instead of asking again tomorrow. And if you've asked the same thing three times, stop asking. Build the dashboard.
The one thing to remember
Share the structure. Share the statistics. Share the problem. Keep the records.
AI doesn't need your data to be useful to you. It needs to understand the shape of your data and what you're actually trying to do with it. Everything else is a habit you build once and then stop thinking about, the same way you stopped thinking about not emailing your password to yourself.