Performance marketing agency: money and data under the control of code
We do not name the client under the terms of the contract: a performance marketing agency running client campaigns across several ad networks and working with an MMP platform. A quality control system for its money and data was built here. Spend-to-top-up reconciliation, automated tests of internal tools, monitoring with alerts and an access audit run as code on a schedule: what used to be checked by hand and by eye is now checked automatically and to the cent.
Results in numbers
- More than a dozen scheduled jobs: spend sync several times an hour, a deep nightly sync, reconciliation, digest, backups, bot health checks, client books, a daily MMP export.
- Around two hundred automated checks in pytest.
- Tens of thousands of lines of own code.
- Every ad account reconciled with partners to the cent: each top-up in the reconciliation book carries a transaction hash or a billing confirmation.
- All buyer sheets fixed in one script run, together with the template.
- About half of the excess MMP accesses closed, with no employee losing access they need.
Defects found and closed
Errors nobody saw until the data started being checked systematically. Each affected money or client decisions; each became a check rule once fixed.
- A campaign renamed within a day produced two rows with the same key, and the write overwrote one of them: the panel lost a noticeable share of spend and overstated balances. Rows with the same key are summed, with a regression test for the case.
- Top-ups were bound to the journal row number: inserting a row sent money to someone else's account. Binding moved to the transaction hash with two-phase matching, verified by a run on a database copy with shifted rows.
- The MMP export added server and SDK events together: the client's registrations and FTDs were roughly doubled. Only the server postback counts now, and the rule is written into the export itself.
- The total in the buyer sheet template stopped halfway down the offer list, so the summary understated costs. Fixed by script in every sheet and in the template.
- Summaries read broken links to other sheets and showed zeros for months. Data is read from the source sheets, and the summary is rebuilt every hour.
- Panel forms silently lost fields on save. An audit of four classes of loss followed, with a fix and a test for each.
- The panel database in the container was mounted as a file in WAL mode, and writes were lost. The mount moved to a directory, with a checkpoint after manual edits.
- Reconciliation for one account counted a failed payment, and the discrepancy with the partner equalled exactly its amount. The counting rule: completed payments only, and an invoice for spend is not a top-up.
Reconciling money to the cent
Reconciliation with partners covers every account: spend, crypto top-ups and card top-ups. The ad network does not expose card top-ups through the API: checking dozens of endpoints and the difference between 403 and 404 responses showed the problem was not access rights. The source was found in the billing interface together with the counting rules, and card payments entered both the reconciliation book and the panel.
Reconciliation is built into the working tool: account balances in the panel match the book to the cent, and when an account is closed its spend history is exported automatically — partners revoke keys without warning.
Automated tests and invariants
Tests describe behaviour, not code. For closing an account, the tests check that editing an active card does not trigger a history export, closing does, a repeated save does not duplicate, and a Google failure does not block closing and goes to the chat as an alert. Covered: card saving, money binding, low-balance notifications, export, observer mode and API limit handling.
- A backup is taken before any bulk edit of money or access.
- The edit runs with an invariant: totals before and after must match.
- The result is re-read from the system, not taken from the script's reply.
- If the invariant fails, the change is rolled back.
Monitoring, limits and API diagnostics
A failure of any job goes to the admin chat through a shared alert module: the problem is visible when it occurs, not when a client asks about a balance. External API limits are handled rather than crashing the process. One of the networks has a single limit shared across all accounts, with waits of up to half an hour. The MMP's limit is frequency-based: measurement showed which pause between requests lets the whole stream through, and on refusal the export substitutes the last successful one and marks it explicitly.
The integration diagnostic method is a control request with a deliberately wrong token and an analysis of the 401, 403 and 404 codes, each of which means something different. This established that part of the platform's API is not available to agency accounts and found a working alternative path. Checking for missing apps by a single country gave more than half false positives and was moved to a multi-geo check; campaign blacklists are managed through the API.
Access audit
Several MMP accounts, dozens of users, rights to hundreds of apps: role, last login and a match against the staff list. Access was closed for long-inactive users, those who never logged in and external users without logins. Admins and integration accounts are protected from deletion, a backup of rights is saved before deletion, and the result is verified by a repeat request afterwards. Employees on partner-domain emails are identified by the work address from the staff list, not by the name in the system.
Stack and integrations
- Code: Python, FastAPI, SQLite, pytest, gspread, Google Apps Script.
- Infrastructure: Podman, systemd timers, nginx and Caddy.
- Ad networks and MMP: statistics and campaign management APIs, MMP export and user management APIs.
- Working tools: Google Sheets, Drive and Apps Script, Asana, Telegram Bot API — a spend panel refreshed every 15 minutes, a client bot with balances per account, a top-up journal filled in by transaction hash.
Principles
- Two independent sources: money is reconciled across at least two systems, and no single number is taken on trust.
- Backup, invariant, re-read: every bulk edit passes all three steps or is rolled back.
- Irreversible actions only from a list: deletions and mass messages go out after the list is shown and explicitly confirmed.
- Every defect becomes a check rule so it does not happen again.
Figures and system names are generalized under the confidentiality agreement.
FAQ
How is this different from regular testing?
Why reconcile against two sources rather than one?
How should ad network API limits be handled?
Can such a system be replicated at another agency?
Tell us about your task
We reply within one business day and send an estimate in 1–2 days. Free of charge.