Inspiration
In Lima, hiring an electrician or a plumber runs on WhatsApp and word of mouth. That works until something goes wrong, and then three things break at once: the professional has no reputation he can carry to the next client, the client has no guarantee the work will be done, and if there is a disagreement over a S/80 job, nobody resolves it — because resolving it costs more than the job was worth. The relationship just ends.
The people this hurts are not marginal users. They are the majority of the services economy in Peru, working informally because the alternatives charge fixed fees they cannot cover on a slow week.
We built Tchasky around a specific bet: the binding constraint is not demand, it is trust — and trust is mostly a dispute-resolution problem that has never been economically viable at small ticket sizes.
What it does
A client publishes a task. A local professional makes an offer. The client pays into escrow, the professional does the work, and the platform releases the funds on confirmed completion, minus a commission.
The commission is tiered by ticket size — 15% under S/50, down to 5% above S/500 — because the cost of servicing a transaction is roughly constant. A flat rate would either make small jobs unprofitable to serve or push the large ones off-platform.
When a dispute is opened, an administrator can request an AI triage: Gemini reads the case — the task, the amounts, the escrow state, what each side claims — and returns a suggested outcome (refund the client, pay the professional, split, needs more evidence, inconclusive) with its reasoning. Every suggestion is stored with the model name, the exact prompt and the raw response.
The AI never resolves the dispute and never moves money. A human administrator decides; every fund movement runs through the pre-existing human flow, unchanged. That boundary is architectural, not a policy note — for a professional here, one disputed job is a day's wage, so an AI error is not a rounding error. What the AI removes is the cost of thinking about the case, which is exactly what makes small-value resolution impossible today.
How we built it
A pnpm monorepo with three applications sharing one contract package:
- API — Express + TypeScript, PostgreSQL with Drizzle ORM, Redis for queues, deployed on Railway.
- Web — React + Vite + Tailwind, deployed on Vercel, live at tchasky.pe.
- Mobile — React Native with Expo, iOS and Android, sharing validation logic with the web through the shared package.
Gemini is called through its OpenAI-compatible endpoint from disputeTriageService.ts, and the response is parsed against a strict schema — anything unparseable is recorded as inconclusive rather than guessed at.
The part worth describing is who wrote it. This is a one-person company running a multi-model AI pipeline where each model has a fixed role and no model validates its own work: one coordinates and audits, another executes on the repository, others handle research and bulk mechanical tasks. Around them we built a retrieval system over the project's own documentation and a mechanical verifier that re-checks written claims against reality — currently 91 anchored assertions, 90 verified true, 0 false.
That verifier exists because of a failure mode we hit early: an AI-run pipeline does not fail by writing bad code, it fails by confidently repeating facts that stopped being true. So the facts get re-measured instead of remembered.
Challenges we ran into
A 401 does not prove an endpoint exists. We nearly reported the Gemini feature as deployed because the route answered 401 instead of 404. Then we tried a route we invented — and it answered 401 too, because under /admin authentication runs before routing. The negative control is what saved us from claiming something we had not verified.
Railway does not record which commit is running. A deployment pushed by CLI carries no repo, branch or commit metadata, so "what is in production?" had no mechanical answer. We fixed it by making the service declare its own commit: GET /health now returns it, and the claim is anchored to a check that turns false the day someone deploys without it.
Our own contingency plan did not work. Reviewer accounts were supposed to be unlocked by one flag. Reading the code against the reviewer walkthrough, that flag lifted one barrier out of three — a fresh account is also gated by pilot-approval status, so publishing a task returns 403. The walkthrough would have died at step 2, on submission day, with a judge on the other side. A documented workaround that has never been exercised is not a workaround.
Searching for UI strings inside an Android bundle lies in both directions. Production React Native bundles are Hermes bytecode with an overlapping string table: four of five feature strings were found, the fifth returned zero, and the feature was there.
Accomplishments that we're proud of
The complete money cycle runs in production: a S/25 task went from publication to released payment in 61 seconds, splitting S/3.75 in commission and S/21.25 net to the professional.
The regulatory obligations that usually stop Peruvian platforms are implemented rather than deferred — the consumer complaints book, electronic invoicing, and tiered KYC.
And the boring one that matters most: 1,807 automated tests across the three applications — 1,055 API, 446 mobile, 306 web — with typecheck clean on all three.
What we learned
"It's committed" is not "it's saved." Two tools announced as existing lived on a single disk; the commit had never been pushed. One of them was the only way a reviewer could access the product.
A confident answer gets quoted; a blank one gets noticed. Our retrieval system answered a licensing question with the wrong license, sourced from a document that mentioned it in passing. Re-indexing did not fix it — anchoring the value to a mechanical check did.
The measurement you skip is the one that embarrasses you. We described our take rate as 15% because the one transaction we had measured was a S/25 job. It is a tiered function; 15% is only the smallest bracket. Projecting a market at that rate overstates it by roughly half.
What's next for Tchasky
Not features — users. Every hypothesis in the product is instrumented and unmeasured, because self-signup is still gated behind a billing decision on SMS verification. The next step is the first hundred professionals in a single district, since density, not city-wide reach, is what makes a hyperlocal marketplace work.
Then the measurement the AI triage was built for: the agreement rate between what Gemini suggested and what the human administrator decided, and resolution time with and without it. Both are already stored separately, precisely so the answer can come out against us.
Built With
- bullmq
- cloudinary
- culqi
- drizzle-orm
- expo.io
- express.js
- firebase
- gemini
- google-cloud
- leaflet.js
- mercadopago
- node.js
- postgresql
- posthog
- railway
- react
- react-native
- redis
- sentry
- socket.io
- tailwindcss
- typescript
- vercel
- vite
- zod
Log in or sign up for Devpost to join the conversation.