Nox

All work Bakeano · Argentina · Marketing agency · Live

Twelve clients. Twelve different spreadsheets. One dashboard.

Exports parsed by column name, not position, because no two files agree.

12 1 Client accounts unified on one multi-tenant platform
The problem

Exports that fight back

Bakeano runs paid advertising for a dozen client accounts across Meta and Google. Every month, reporting meant downloading exports, opening them one at a time, reconciling numbers by hand, and rebuilding the same deck twelve times.

The exports do not cooperate:

Meta files, Spanish headers, column sets differ per client12–20 columns
Same field, two different labels depending on accountnormalise
Google exports, UTF-16, tab separatedmetadata + footer rows
Argentine number format, comma as decimal1000× errors

Any one of those breaks a normal importer. All of them together is why this was still being done by hand.

What I built

A multi-tenant reporting platform

  • Ingestion that expects a fight

    Files are parsed by column name rather than position, because positions differ per client. Header variants are normalised. Metadata and footer rows are detected and dropped. Locale-specific number formats are handled explicitly, not guessed.

  • Isolation at the database level

    Every tenant-specific record carries a tenant identifier and every client-specific record carries a client identifier. Isolation is enforced by row-level security in Postgres, not by application code remembering to filter.

  • Anomaly detection

    Six rules watch for spend spikes and collapses, cost-per-click jumps, reach collapse, conversion drops and accounts going silent, with guards against divide-by-zero on empty periods. A consolidated digest goes out grouped by severity. A whole account going dark bypasses the digest and alerts immediately, because that one can't wait until tomorrow.

  • Reporting that leaves the building

    Per-client dashboards with period-over-period comparison, campaign breakdowns and multi-metric overlays. PDF export for client delivery. Passwordless magic-link authentication. Interface in Spanish and English.

META .XLSX GOOGLE .CSV 12 LAYOUTS PARSE BY COLUMN NAME normalise headers · locale numbers POSTGRES + ROW LEVEL SECURITY tenant_id · client_id enforced DASHBOARDS ALERTS PDF HUMAN APPROVES AI INSIGHTS
Ingestion to delivery, with a human gate
The gate

AI that has to ask permission

The platform generates written insight summaries over the data. Those summaries do not reach a client. Not until a human reads them, edits them if needed, and approves them.

Approval state lives in the database. An unapproved insight is not degraded or watermarked, it is simply absent from the client view and from the PDF. Editing an already-approved insight automatically returns it to draft and pulls it from client view until it is approved again, so an edit can never quietly ship unreviewed.

This was the client's requirement, and it was the right one. An agency's reputation rides on what it hands a client. Generated text going out unreviewed under their name was not acceptable, and the system enforces that rather than relying on someone remembering.

Results

What's running

Client accounts on one platform12
Manual monthly reconciliationreplaced
Anomaly rules6
Tenant isolationrow-level security
AI insights reaching clients unreviewed0
Interface languagesES / EN
Honest scope note

The monthly ingestion of real client exports is live and in use. The daily metrics pipeline, including anomaly detection and digests, is built and tested but running against generated data pending API credentials from the platforms. Switching it to live data is a configuration change, not a build. I'd rather say that than let you assume otherwise.

Transferable

Anywhere the source data arrives as inconsistent files

Any business reporting across accounts, locations or clients where the data comes as file exports rather than a clean API. Franchise reporting, multi-location retail, agency reporting, distributed sales operations.

Rebuilding the same report every month?

If the data arrives as files, it can arrive as a dashboard instead.

Start a diagnostic More work