Builder
Viya
Multi-tenant B2B SaaS that turns raw manifests into reviewed passenger data, optimized routes and driver dispatch.
Status: Early MVP, built as a product for tour agencies beyond a single customer
The problem
Tour agencies work from messy Excel and PDF manifests. Each day's passenger data has to be cleaned up, routed and sent to drivers. Express Ops solved this for one operator; Viya rebuilds the same domain as a multi-tenant product for any agency.
What I built
Viya generalizes the Express Ops domain into a SaaS for any tour agency: ingest Excel or PDF manifests, extract structured data with a routable set of LLM and OCR providers, plan routes and dispatch drivers over the messaging apps they already use.
OutcomeA working early MVP that covers manifest to driver dispatch end to end. The repo's notes list 59 backend test files (Vitest and convex-test), and CI/CD is configured but not yet cutting over production traffic. No customers or revenue are claimed.
What it does
- Four parse providers (Docling, native sheet, Mistral OCR, LlamaParse) and five LLM extraction providers behind one routing layer.
- Route generation with manual reordering on a dual map stack: Google Maps and MapLibre with OSRM directions.
- Driver dispatch over WhatsApp, Telegram and Slack, including inbound status replies and interactive confirmations.
- Fleet and capacity management; weather widget backed by MET Norway data with caching.
- Python FastAPI microservice for document parsing.
Try it
A working replica of the real interface, running on made-up sample data.
Architecture
How the pieces connect, drawn from the project's own documentation.
Engineering decisions
- Route extraction across five LLM providers behind one layer (MiniMax by default, with a fallback chain across the others) instead of tying the pipeline to one vendor.
- Give spreadsheets a fast path: structured Excel files can skip much of the OCR and LLM work that photographed or PDF manifests need.
- Bound the cost of large documents: token budgets and timeouts scale with input size, big non-spreadsheet files are extracted in chunks and merged with de-duplication, and oversized parse output is truncated with an explicit flag.
- Block route generation until every passenger's location is resolved. Missing, failed or ambiguous geocodes stop it; low-confidence results only warn.
- Protect the geocoding stack with a circuit breaker and provider-aware caching, and score confidence on one model across Google, Nominatim and Pelias so labels stay consistent.
- Isolate tenants first: Clerk organization context plus Convex ownership guards on every business read and write, and channel credentials encrypted before storage.
- Use Slack as per-driver direct messages with signature validation and duplicate suppression, rather than a shared-channel workflow.
How it's secured and shipped
- Clerk authentication with tenant-scoped access controls in the Convex backend.
- Sentry across browser, server and edge runtimes, plus the Python service, with documented privacy rules.
- Separate local, staging and production stacks; local backend and dashboard bind to loopback only.
- Security best-practice, performance and UI/UX audit reports kept in the repo.
- Linting and static analysis configured for both TypeScript and Python (ESLint, flake8, mypy, DeepSource).
What's next
From the project's own roadmap.
- Chain extraction server-side right after a successful parse; today the client triggers it when the review page mounts.
- Add a multi-tenant security regression suite covering IDOR attempts and cross-tenant foreign-key linking.
- Scheduled dispatch for a future date and time.
- Tooling to rotate the encryption key and re-encrypt channel credentials.
- Team invites and role-based privileges for organization members.
Stack
- Next.js
- TypeScript
- Convex
- Clerk
- Python / FastAPI
- Docling
- MapLibre
- Sentry
- Docker