

Handi
Book a plumber or electrician by chatting, with search and payment in the same conversation
The problem
Home-services marketplaces push people through forms, filters and checkout flows. Both sides would rather just message. Turning free-form conversation into reliable booking, search and payment is the hard part, and it is not a thin wrapper around a chat model: it needs an agent architecture with real authorisation, real state, and real failure handling.
What I built
Handi is an app-based marketplace where customers find plumbers, electricians and other professionals by talking. Professionals manage bookings, availability, pricing and their profile the same way. Registration, semantic search, scheduling and payment all happen in the conversation, across Hebrew, English, Arabic, Spanish and French.

The agent graph
The backend is a NestJS monorepo with a LangGraph supervisor running every message through the same path:
- Preprocess: security checks, blocked-user handling, input sanitisation, message persistence
- Route: identify user type (customer, professional, or new) and load conversation context
- Validate: gate paused, blocked or archived professional accounts
- Subgraph: a domain-specific agent loop for customers or for professionals
- Respond: output auditing, delivery, token accounting
Each subgraph builds its prompt from PostgreSQL conversation history, exposes tool metadata from a central registry, and lets Gemini orchestrate the tool calls.
The tool registry
Tools live as pure executors: search, booking, customer, professional, conversation, messaging. Each class declares its function-calling schema and delegates to a service. Tools may depend on shared infrastructure but never on each other, which keeps the dependency graph acyclic and makes authorisation explicit rather than inherited.
Every tool call validates the authenticated phone number, so a user can reach only their own bookings, profile and conversations. Rate limits and audit logging apply per tool rather than per request.
Search and matching
Matching combines SQL filters (rating, price, experience, service area) with vector embeddings over bios, specialisations and availability patterns. The model then re-ranks against conversation context, urgency and customer profile before presenting options as a message rather than a results grid.
Security by design
Conversational interfaces widen the attack surface, so the guardrails are layered: regex and model-based input classification, phone-based data isolation, output auditing for leaked personal data, and automatic temporary blocks on high-risk behaviour. The security rules sit at the top of every agent prompt, and authorisation checks are never skipped for convenience.
Deployment
Backend and frontend deploy to Google Cloud Run through GitHub Actions, with PostgreSQL holding all state and S3 for uploads. The agent layer stays testable and observable through tracing, which matters more than usual when the product surface is a conversation and every bug report is a transcript.
Building something in this territory? Start a conversation →
