Inspiration
Everyone negotiates salary offers, rent increases, freelance invoices, refunds, security deposits, but almost nobody is trained for it. The people who lose the most in these conversations are exactly the ones who can't afford a lawyer or a negotiation coach, and they usually realize they were negotiating only after they've already sent the wrong reply.
We kept seeing the same scene: someone staring at a WhatsApp message from a landlord, a client, or a boss, typing and deleting the response for twenty minutes. Meanwhile, every negotiation app on the store is a course or a practice dojo drills for a hypothetical future case. Nobody builds for the negotiation that is happening right now, inside the chat thread itself.
Consilium is our answer: an AI negotiation coach for real, live cases with a floating tactical pill that sits on top of whatever chat app the dispute is actually happening in.
What it does
Paste a situation or capture the thread you're looking at with one tap on the overlay and Consilium routes your case to one of seven specialist coaches: freelance, payment, employment, housing, consumer, debt, or contract review. You chat back and forth as the other side replies, and on demand the coach produces a structured Analysis: your bargaining power, the context breakdown, and three ordered tactical cards hold, trade, or walk that you can act on immediately.
The differentiator is the pill. One tap captures the conversation on screen, extracts the text on-device with ML Kit, and drafts your next move without ever leaving the chat app. Screenshots never leave the phone analysis only runs when you press the button.
How we built it
Android app. Kotlin + Jetpack Compose with a modular architecture (feature api/impl modules over a shared core), Hilt for DI, Room for local persistence, DataStore, WorkManager sync, and Navigation 3. The floating pill is a real overlay: MediaProjection for capture, ML Kit text recognition on-device, Accompanist for the permission flow, and a state machine driving the pill's lifecycle across arbitrary chat apps. Networking is Retrofit/OkHttp with kotlinx.serialization; Firebase covers auth entry points, Crashlytics, Analytics, and Performance.
Agent backend. The public API is a FastAPI gateway running an embedded LangGraph runtime (harness). A lead agent holds the conversation and delegates to the specialist that matches the case category the routing is deterministic: the model never picks its own specialist. Replies stream to the app over SSE. Behind every specialist call sits a guardrail middleware: safety screening, quota admission, LLM budget admission, and a kill switch. Structured analysis goes through a repair pipeline across multiple LLM providers, with transport-level failover kept separate from schema repair.
Data & auth. PostgreSQL on a self-hosted server, managed by Alembic migrations, with asyncpg/SQLAlchemy and Redis for budget state. Users sign in with email/password or Google ID-token exchange and receive Ed25519-signed short-lived access tokens with 30-day rotating refresh tokens reuse of a refresh token revokes the whole family. Tokens are stored encrypted on-device.
Monetization & retention. RevenueCat powers the paywall: free users get a small daily analysis quota, Consilium Pro raises it to 100 analyses/day. Quota is server-authoritative committed at turn admission, refunded if the run errors or ends in a clarification, never trusted to the client. OneSignal push brings users back when their quota resets, closing the retention loop.
Challenges we ran into
- Deterministic routing through a probabilistic agent. The lead agent had to always reach the right specialist. We solved it at the middleware layer: the thread's case category forces a delegation instruction, off-route delegations are rewritten, and a bounded retry closes the gap no reliance on model goodwill.
- A denial the model can't talk its way around. When quota is exhausted, the guardrail returns a terminal denial carried in a typed SSE envelope that the app maps straight to the paywall on both the tool-call surface and the per-turn model surface, so no model reply can sneak past the limit.
- Migrating production off Supabase mid-flight. We moved the canonical database to a self-hosted Postgres instance and verified byte-identical parity before retiring the old path including re-learning that without a pooler,
pg_advisory_lockis safe for our migration pattern. - One capture pipeline, three entry paths. Paste, share-sheet, and MediaProjection capture all had to converge on the same canonical thread/SSE analysis flow unifying them early saved us from three subtly divergent result screens.
- Streaming an agent run as a product. Typed SSE frames, clarification terminals, and typed error envelopes then mapping them cleanly onto Android state took deliberate boundary design between the LangGraph runtime and the Retrofit layer.
Accomplishments that we're proud of
- A pill that works over any chat app. Three intake paths paste, share sheet, and MediaProjection screenshot converge on one canonical thread/SSE analysis flow, so the result is identical no matter how the case arrives.
- Quota a hostile client can't beat. Turn admission commits server-side, errored and clarification-only turns get refunded, and terminal denials can't be argued past by the model.
- Bilingual from day one. The app ships in English and Indonesian, matching the market where the intent gap is widest.
- Privacy that survives inspection. OCR runs on-device with ML Kit; the screenshot of a private chat never leaves the phone.
What we learned
- The moat is moment-of-need, not content. Our ASO research mapped the whole negotiation/coaching category on Google Play: every incumbent is a course or a training dojo, and the dispute-resolution shelf is nearly empty. "Coach-in-the-moment for a real case" is unclaimed whitespace so we built the product and the listing around that claim instead of fighting over "negotiation skills."
- Quota is a distributed-systems problem, not a UI counter. Charging at the right boundary took a run-scoped charge registry, terminal-denial envelopes so a denied turn can't be answered past an exhausted quota, and refunds on errored or clarification-only turns. Client-side quota would have been trivially bypassable and permanently confusing to users.
- On-device privacy is a feature you can feel. Keeping OCR on the phone with ML Kit let us make a promise most AI apps can't: the screenshot of your private chat never leaves your device. That constraint shaped the whole overlay UX for the better.
- Prompts are configuration, not code. Specialist instructions and the lead agent's persona are versioned, auditable rows in Postgres with a production pointer we can promote or roll back a prompt without redeploying.
What's next
Shipaton is our traction loop: the store listing is keyword positioned at the empty "negotiation coach for real disputes" shelf, OneSignal reset notifications drive the return habit, and the free tier is sized to let the product prove itself in one real negotiation. We're also tracking a web-funnel checkout path so users can subscribe before they ever open the Play listing.
Consilium is an AI coaching tool, not a law firm it provides negotiation strategy, not legal advice.
Built With
- authenticated-webhook-to-our-backend)-kotlin-jetpack-compose-android-credential-manager-(google-sign-in)-ml-kit-on-device-text-recognition-onesignal-(push-re-engagement
- consent-gated-subscription-linking)-fastapi-(entitlement-and-quota-backend
- entitlement-"pro"
- postgresql
- python)
- revenuecat-sdk-(google-play-billing
- subscription-pro-monthly-/-base-plan-monthly
Log in or sign up for Devpost to join the conversation.