case/experiences/batameta-tecnologia
01/08
Batameta Tecnologia·São Paulo, BR·Dec 2025 - Presentcurrent

Founder & Tech Lead

Fintech I built from zero for Brazil's gig-driver market 4 apps, native Android Copilot, dual-rail payments.

4
apps in production
R$ 250k+
tracked driver earnings
60k+
km logged
Founder
contract

I was working full-time CLT and looking for a problem worth founding a company around. I started talking to gig drivers on work trips - new ones, veterans, all of them - and the same pain kept coming back: nobody had clarity on whether they were actually profitable. They accepted whatever ride paid most this minute and discovered at month-end that fixed costs hadn't been covered. Batameta started there: a tool that calculates the daily target a driver needs to hit to pay their real bills on time, plus a native Copilot that grades each Uber/99 ride offer the second it arrives. Building it took me into territory I hadn't owned before - native Android bridges to read screens we don't control, anonymous telemetry to validate parsers on devices I'll never own, an Open/Closed architecture so the next ride app plugs in without a rewrite. Today four apps are live, drivers are using it daily, and I'm learning that being a founder means engineering is the easier half.

01

The problem we picked up

Talking to drivers on the road, the same scene kept repeating. A driver has a R$ 800 bill due on day 8 and tries to plan by dividing it across the rest of the month - but the math is wrong, the daily working hours don't match, and the bill quietly slips. Multiply that by recurring costs (rent, insurance, fuel) and a few non-recurring shocks (a tire, a service), and most drivers can't actually tell whether they cleared their costs in any given week.

02

Goal engine

Monthly target = recurring + non-recurring + desired profit + bills postponed from previous months. Daily target = (monthly − already-earned) ÷ remaining working days, taking into account each bill's actual due date and the driver's chosen working pattern. When the monthly target is hit, an extra-daily kicks in based on the driver's own average earning. Every recompute lands as a push: 'your goal today is R$ X' - the whole financial model becomes a single number on the driver's wrist before they go online.

4
apps in production
03

Challenge: reading apps we can't modify

The Copilot needs to read each Uber/99 ride offer the moment it appears, in real time, on the driver's phone - and we don't own those apps. We can't ship code into them, can't use a public API, and we don't want screenshots leaving the device for privacy and cost reasons.

04

Solution: native Android bridge

Built a native Kotlin module that React Native calls into. An AccessibilityService watches Uber/99 in foreground, traverses the view tree, and falls back to ML Kit OCR when the tree is locked. Per-app parsers (UberParser, NinetyNineParser) pull price, distance, time, pickup, destination, rating. Evaluation runs 100% on-device against the driver's cached cost profile. The API only serves parser configs (cached 1h) - zero per-ride calls means privacy stays intact and the Copilot keeps working on flaky connections.

05

Challenge: trusting parsers on devices I'll never own

I test on my own device. Users have thousands of others - different Android versions, different manufacturers, different OEM tweaks to the view tree. How do I know the parser is actually getting it right out there?

06

Solution: anonymous telemetry pipeline

Every time a ride offer hits the Copilot, with the user's explicit consent, the device sends a minimal payload: the raw extracted screen data, the parsed output, app version, copilot module version, Android version, manufacturer, raw and parsed pickup/destination addresses. Fully anonymized - we have no way to map a payload back to a user. Comparing raw vs parsed at scale tells us exactly which devices a parser is missing on, and where to harden it next. The Copilot gets better the more it's used.

07

Challenge: every ride app changes its UI without warning

Uber updates a layout. 99 ships a redesign. iFood and InDrive look nothing alike. Hard-coding parsers against today's view tree means breakage in a week.

08

Solution: Open/Closed architecture + hot-swap parsers

The Copilot core is closed - it knows nothing about specific apps. Each integration plugs in as a parser implementing a single contract. Parser configs ship from the API and refresh hourly, so a UI break is fixed without an app release. Adding a new ride app (iFood, InDrive, future ones) is a parser drop-in, never a rewrite.

09

Backend platform

Event-driven NestJS + Prisma over PostgreSQL with 7 isolated schemas (customers, marketing, goals, finances, admin, payments, public) - bounded contexts get clean ownership boundaries. RabbitMQ decouples async work, MongoDB stores high-write event logs, OpenAI handles ambiguous address enrichment in a separate queue. Payments are agnostic-by-design: Stripe handles cards, AbacatePay handles BR-native PIX (cobrança + recurrence + webhooks). Both rails idempotent, reconciliation identical regardless of provider. Activation orchestrates Expo push, WhatsApp (WAHA), React Email + Resend, plus a referral/reward engine and a gamification module (badges, levels, streaks).

10

What I'm learning as a founder

Engineering is the easier half. The harder half is connections, conversations and putting the product in front of people. I have a co-founder now who runs marketing and growth - the inverse of my profile - and the lesson that keeps repeating is that a product isn't the prettiest thing on the screen, it's the easiest thing to use that solves a real demand. The customer decides what's good. My job as CEO/CTO is to translate that into technology.

decisions & tradeoffs
  • Why a native Android Copilot instead of in-app notifications?
    Kotlin AccessibilityService + ML Kit OCRAn offer evaluator needs to read the Uber/99 screen the moment it appears without modifying their apps and without screenshots leaving the device. AccessibilityService is the only stack that gets us there. Local-only evaluation keeps it private and removes per-ride API costs entirely.
  • Why dual-rail payments (Stripe + AbacatePay)?
    Stripe + AbacatePayStripe is the easy international card rail, but PIX dominates BR and recurring PIX via AbacatePay is dramatically cheaper than card processing for the driver persona. Both rails run webhook-driven with idempotency, so reconciliation is identical.
  • Why multi-schema PostgreSQL (7 schemas) over a single schema?
    Schema-per-bounded-contextCustomers, marketing, goals, finances, admin, payments and public live in their own schemas. Bounded contexts get clean ownership boundaries; backups, migrations and even per-domain access roles stay simple. Adding a domain is a schema operation, not a re-architecture.
  • Why parser config server-side, evaluation client-side?
    Hot-swappable parsers, local evaluationUber/99 change their UI without warning. Pulling parser configs from the API means we ship a fix without an app release. But evaluation stays local so user data never leaves the device and we don't pay rate-limit costs per ride.
  • Why an opt-in raw + parsed telemetry pipeline?
    Anonymous payload, raw alongside parsedI can't validate parser quality against an Android device ecosystem from a single phone. Sending the raw screen data alongside the parsed output - anonymized and consented - lets us see at scale where parsing diverges, on which Android versions, manufacturers and app builds. The Copilot improves the more it's used, instead of degrading every time a vendor ships a new layout.

From idea to operational fintech: 4 apps live, drivers using it daily, R$ 250k+ in tracked earnings and 60k+ km logged so far. Native Copilot in users' hands, agnostic dual-rail payments processing, ~76 specs guarding the codebase, Open/Closed architecture ready for the next ride app to plug in.